Import Tint changes from Dawn

Changes:
  - 55c0c9d950edb4f45b2f94d5c3e4114fc30609e8 tint: Add abstract numerics to vector and matrix construc... by Ben Clayton <bclayton@google.com>
  - d3de38d7e3880a90cbed8f8fb895729e580c8fbd tint: Simplify the resolver constant evaluation by Ben Clayton <bclayton@google.com>
  - 8f4f4495408cfde7755be8127180178015bf7d40 tint/writer: Do not attempt to use invalid programs by Ben Clayton <bclayton@google.com>
  - 6522837acbf96682583ada4ead5c8e86f28df437 tint: Add `enable_abstract_numerics` flag on Resolver by Ben Clayton <bclayton@google.com>
  - 43581f1fb62ced507e1693c2e8959ed342dd7f6a tint: Add new methods to semantic Switch nodes by Ben Clayton <bclayton@google.com>
  - a2ce4ecc8b325ff79176a867b6074b6ca733851b tint: Add more helpers to resolver_test_helper.h by Ben Clayton <bclayton@google.com>
  - d99af036634c3dc612d18042465bfcc45203fffc tint: Add utils::UniqueVector::data() by Ben Clayton <bclayton@google.com>
  - ab4c0357629178ec2c217b4b05ad715142546fe1 tint: fix HLSL countOneBits and reverseBits for i32 args by Antonio Maiorano <amaiorano@google.com>
  - e9ce8326b7843be34e8a31a5a54600cb924c83f2 tint: Minor, miscellaneous cleanups by Ben Clayton <bclayton@google.com>
  - eee9f88ba21b182d7eb23bbc85af17f69185e34a tint: Extract intrinsic-table common type to helper by Ben Clayton <bclayton@google.com>
  - 1b35e3f9a8c10c11d750fde76f0862e8e9bc9534 tint: Add new sem::Type helpers by Ben Clayton <bclayton@google.com>
  - e5a67ac891a5c8aa584b7998d35453a29b778a93 tint: Remove ast::CallExpression -> sem::Call implicit ma... by Ben Clayton <bclayton@google.com>
  - 7b921fb4c778b324d706f78a91d5e047626f2292 tint: No-op Resolver refactoring by Ben Clayton <bclayton@google.com>
  - 86a617f1108cbfa49c8c1faebc7b4af8acd36588 Add InsertBraces: true to .clang-format by Austin Eng <enga@chromium.org>
  - 2081ee43bf51005e2dbed2b271254d517e998fd0 tint: Add sem::Materialize by Ben Clayton <bclayton@google.com>
  - 6ac00ed0c0cff0401da91b4faad649d0ea81f2d5 tint: IntrinsicTable: Add abstract numeric types by Ben Clayton <bclayton@google.com>
  - b1fa457ab3c362a12bd885254352fc617d89f808 tint: IntrinsicTable: Use [[display]] name for type match... by Ben Clayton <bclayton@google.com>
  - b0664684cd887f8c8163e70b64c797852b86e276 tint: Support tuples in utils::Hash(). by Ben Clayton <bclayton@google.com>
  - 7f2b8cd8fc8edf464602ea64d2145def40fa3475 tint: Refactor Extensions / Enables. by Ben Clayton <bclayton@google.com>
  - 23696b1ba3fd523f9f989677890af18db0d68d7a tint: Implement abstract-numeric overload resolution by Ben Clayton <bclayton@google.com>
  - 2e681052b313ed547189d95d58d73081c028d604 tint: Fix use-after-free by Antonio Maiorano <amaiorano@google.com>
  - c670018aea9f3ff989699b47c0b5d5a977213cc8 tint: intrinsics.def Support [[precedence]] decoration by Ben Clayton <bclayton@google.com>
  - 5ff7d67bf341af630c8945296155b77edc13ef81 tint: Validate that sampled texture type must be f32, i32... by Antonio Maiorano <amaiorano@google.com>
  - e0ff664a7f0dee5ac9c3c19e48121b6cfca89c31 tint: Add sem::Type::ConversionRank() by Ben Clayton <bclayton@google.com>
  - 4c9ed74b5e5bded47aa034026a6ea80b27dd666b tint: IntrinsicTable: Rename open/closed -> template by Ben Clayton <bclayton@google.com>
  - aaa9ba30436bdb23a59b9eaa4e65f1808cba0dec tint: Simplify sem::Constant::Scalar by Ben Clayton <bclayton@google.com>
  - 661e33ca185f766019bd27fedda57a354efed787 tint: Cleanup of IntrinsicTable by Ben Clayton <bclayton@google.com>
  - 8ba6e1d6ecdd3f9438b5034d32b301152cb931f8 tint: limit expression depth to avoid stack overflow in b... by Antonio Maiorano <amaiorano@google.com>
  - 5880ed164abb36f816bbea1a715ad7bc988c3648 tint: Fix edge for CallSiteRequiredToBeUniform by James Price <jrprice@google.com>
  - 3b5edf143505f6b97f065e83de0d63f647fd3e50 tint: Add matrix identify and single-scalar ctors by Ben Clayton <bclayton@google.com>
  - 6ae608cb03840d1f8b075289f4445f149ab33f0a tint: Add constructors and conversions to the intrinsic t... by Ben Clayton <bclayton@google.com>
  - 9ff8abf34711e4e740b68c10fc0c90bbc441c7e5 tint: Fix clang chromium-style warnings treated as errors by Ben Clayton <bclayton@google.com>
  - e6b6777c8e56b0940315b9fef97a13e021c61747 tint: Fix MSL generation of '&' and '|' with booleans by Ben Clayton <bclayton@google.com>
  - 35f0fcaac0f6e0d0a56e8f5f7c00cd297256d518 tint/uniformity: Use non-recursive graph traversal by James Price <jrprice@google.com>
  - a89ff5981bc0c17e980a5dbdb4f2da870ed79db0 tint: Show where control flow became non-uniform by James Price <jrprice@google.com>
  - 1c75921db9c0095d7ab96c686f51efa2951b2f4f tint: fix translation of DP4a on HLSL by Jiawei Shao <jiawei.shao@intel.com>
  - d8e77e2e73afd5bb2238ac322d7acbdd084ec410 tint: Prevent integer overflow in IntrinsicTable by Ben Clayton <bclayton@google.com>
  - 83fc247d4bb9ab23616a697f272d69f29f44447f tint: correctly define user-defined literals by Antonio Maiorano <amaiorano@google.com>
  - 59e23943f3b1442281208e879e6bc4c5f478ca91 tint: Minor IntrinsicTable cleanup by Ben Clayton <bclayton@google.com>
  - 77473b469958b242509c9eb9271bbcf908e791de tint: Split tables for unary and binary operators by Ben Clayton <bclayton@google.com>
  - 62bfd318aefae4b5a1f50813f90bf0b8b821849b tint: Implement `f16` keyword in Tint frontend by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - c4b380b8af94fe8b7a61ae8bd3f735f9d7a51380 tint: Generalize sem::Vector to allow 16bits subtype by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - ed6ff9c948005a23fb2d592c5ea069d778bca083 tint: Rename kNotAnExtenxion to kNoExtension by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - ab9757036bd6b2fe86c4e937db0524463ba0c0f8 tint: Implement DP4a on HLSL writer by Jiawei Shao <jiawei.shao@intel.com>
  - 53547db1d4f045d93ee408b6a30da25eb88ef056 tint: Add missing source information for | and || by James Price <jrprice@google.com>
  - 816148fe3291d3def83de76584cfe32af8572f49 tint: Add implicit CF_return->{last cf} edge by James Price <jrprice@google.com>
  - cd55f15c98de761c6410e19670398c149aa96dbe tint/resolver: Fix chromium-style warning treated as error by Ben Clayton <bclayton@google.com>
  - 7dd0ab791a59c89b628981f82f56b9ee3e8208b6 tint: Show the source of non-uniformity by James Price <jrprice@google.com>
  - 9c03abfb554fd64adb66a9f11d7e84766cdc15b3 tint: Show the reason for a uniformity requirement by James Price <jrprice@google.com>
  - 874b61f1badfe275c480d1dcb6d32b867391de73 tint/uniformity: Retain control flow graphs by James Price <jrprice@google.com>
  - be656f7984a48e3c4e9a3c9dcc0ba7bbdc3da278 tint: Implement uniformity analaysis by James Price <jrprice@google.com>
  - 8e68f0aad7c3994ddbd1914a48d82c43dd6e7e56 tint: Resolve empty loop continuing blocks by James Price <jrprice@google.com>
  - 2cf32b13d7c294a7a3b4e87f9ab11aebbb519e81 tint: Make ScopeStack key type generic by James Price <jrprice@google.com>
  - 791b4351d1860e1d447f182619e8bd1023dc47eb tint: Add transform to disable uniformity analysis by James Price <jrprice@google.com>
GitOrigin-RevId: 55c0c9d950edb4f45b2f94d5c3e4114fc30609e8
Change-Id: I584598c20d24d8cadb29f63b5e1f46aac06aebb2
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/89880
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: Ben Clayton <bclayton@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 259a26b..fbf443c 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -226,8 +226,12 @@
     "ast/enable.h",
     "ast/expression.cc",
     "ast/expression.h",
+    "ast/extension.cc",
+    "ast/extension.h",
     "ast/external_texture.cc",
     "ast/external_texture.h",
+    "ast/f16.cc",
+    "ast/f16.h",
     "ast/f32.cc",
     "ast/f32.h",
     "ast/fallthrough_statement.cc",
@@ -342,7 +346,6 @@
     "debug.h",
     "demangler.cc",
     "demangler.h",
-    "number.h",
     "diagnostic/diagnostic.cc",
     "diagnostic/diagnostic.h",
     "diagnostic/formatter.cc",
@@ -357,6 +360,7 @@
     "inspector/resource_binding.h",
     "inspector/scalar.cc",
     "inspector/scalar.h",
+    "number.h",
     "program.cc",
     "program.h",
     "program_builder.cc",
@@ -365,6 +369,8 @@
     "program_id.h",
     "reader/reader.cc",
     "reader/reader.h",
+    "resolver/ctor_conv_intrinsic.cc",
+    "resolver/ctor_conv_intrinsic.h",
     "resolver/dependency_graph.cc",
     "resolver/dependency_graph.h",
     "resolver/intrinsic_table.cc",
@@ -375,9 +381,14 @@
     "resolver/resolver_constants.cc",
     "resolver/sem_helper.cc",
     "resolver/sem_helper.h",
+    "resolver/uniformity.cc",
+    "resolver/uniformity.h",
     "resolver/validator.cc",
     "resolver/validator.h",
     "scope_stack.h",
+    "sem/abstract_float.h",
+    "sem/abstract_int.h",
+    "sem/abstract_numeric.h",
     "sem/array.h",
     "sem/atomic.h",
     "sem/behavior.h",
@@ -392,6 +403,7 @@
     "sem/depth_texture.h",
     "sem/expression.h",
     "sem/external_texture.h",
+    "sem/f16.h",
     "sem/f32.h",
     "sem/for_loop_statement.h",
     "sem/i32.h",
@@ -451,6 +463,8 @@
     "transform/decompose_strided_array.h",
     "transform/decompose_strided_matrix.cc",
     "transform/decompose_strided_matrix.h",
+    "transform/disable_uniformity_analysis.cc",
+    "transform/disable_uniformity_analysis.h",
     "transform/expand_compound_assignment.cc",
     "transform/expand_compound_assignment.h",
     "transform/first_index_offset.cc",
@@ -587,6 +601,8 @@
     "sem/expression.h",
     "sem/external_texture.cc",
     "sem/external_texture.h",
+    "sem/f16.cc",
+    "sem/f16.h",
     "sem/f32.cc",
     "sem/f32.h",
     "sem/for_loop_statement.cc",
@@ -600,6 +616,8 @@
     "sem/info.h",
     "sem/loop_statement.cc",
     "sem/loop_statement.h",
+    "sem/materialize.cc",
+    "sem/materialize.h",
     "sem/matrix.cc",
     "sem/matrix.h",
     "sem/member_accessor_expression.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 682817f..85ec3db 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -114,8 +114,12 @@
   ast/enable.h
   ast/expression.cc
   ast/expression.h
+  ast/extension.cc
+  ast/extension.h
   ast/external_texture.cc
   ast/external_texture.h
+  ast/f16.cc
+  ast/f16.h
   ast/f32.cc
   ast/f32.h
   ast/fallthrough_statement.cc
@@ -246,6 +250,8 @@
   program.h
   reader/reader.cc
   reader/reader.h
+  resolver/ctor_conv_intrinsic.cc
+  resolver/ctor_conv_intrinsic.h
   resolver/dependency_graph.cc
   resolver/dependency_graph.h
   resolver/intrinsic_table.cc
@@ -256,6 +262,8 @@
   resolver/resolver.h
   resolver/sem_helper.cc
   resolver/sem_helper.h
+  resolver/uniformity.cc
+  resolver/uniformity.h
   resolver/validator.cc
   resolver/validator.h
   scope_stack.h
@@ -333,6 +341,8 @@
   transform/decompose_strided_array.h
   transform/decompose_strided_matrix.cc
   transform/decompose_strided_matrix.h
+  transform/disable_uniformity_analysis.cc
+  transform/disable_uniformity_analysis.h
   transform/first_index_offset.cc
   transform/first_index_offset.h
   transform/fold_constants.cc
@@ -399,6 +409,8 @@
   sem/depth_texture.h
   sem/external_texture.cc
   sem/external_texture.h
+  sem/f16.cc
+  sem/f16.h
   sem/f32.cc
   sem/f32.h
   sem/for_loop_statement.cc
@@ -409,6 +421,8 @@
   sem/if_statement.h
   sem/loop_statement.cc
   sem/loop_statement.h
+  sem/materialize.cc
+  sem/materialize.h
   sem/matrix.cc
   sem/matrix.h
   sem/multisampled_texture.cc
@@ -681,7 +695,9 @@
     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
@@ -785,7 +801,9 @@
     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
@@ -798,6 +816,7 @@
     sem/sem_struct_test.cc
     sem/storage_texture_test.cc
     sem/texture_test.cc
+    sem/type_test.cc
     sem/type_manager_test.cc
     sem/u32_test.cc
     sem/vector_test.cc
@@ -831,6 +850,13 @@
     writer/text_generator_test.cc
   )
 
+  # Uniformity analysis tests depend on WGSL reader
+  if(${TINT_BUILD_WGSL_READER})
+    list(APPEND TINT_TEST_SRCS
+      resolver/uniformity_test.cc
+    )
+  endif()
+
   # Inspector tests depend on WGSL reader
   if(${TINT_BUILD_WGSL_READER})
     list(APPEND TINT_TEST_SRCS
@@ -1044,6 +1070,7 @@
       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_constants_test.cc
diff --git a/src/tint/ast/enable.cc b/src/tint/ast/enable.cc
index 857e110..ef43200 100644
--- a/src/tint/ast/enable.cc
+++ b/src/tint/ast/enable.cc
@@ -21,37 +21,7 @@
 
 namespace tint::ast {
 
-Enable::ExtensionKind Enable::NameToKind(const std::string& name) {
-    if (name == "chromium_experimental_dp4a") {
-        return Enable::ExtensionKind::kChromiumExperimentalDP4a;
-    }
-
-    // The reserved internal extension name for testing
-    if (name == "InternalExtensionForTesting") {
-        return Enable::ExtensionKind::kInternalExtensionForTesting;
-    }
-
-    return Enable::ExtensionKind::kNotAnExtension;
-}
-
-std::string Enable::KindToName(ExtensionKind kind) {
-    switch (kind) {
-        case ExtensionKind::kChromiumExperimentalDP4a:
-            return "chromium_experimental_dp4a";
-        // The reserved internal extension for testing
-        case ExtensionKind::kInternalExtensionForTesting:
-            return "InternalExtensionForTesting";
-        case ExtensionKind::kNotAnExtension:
-            // Return an empty string for kNotAnExtension
-            return {};
-            // No default case, as this switch must cover all ExtensionKind values.
-    }
-    // This return shall never get hit.
-    return {};
-}
-
-Enable::Enable(ProgramID pid, const Source& src, const std::string& ext_name)
-    : Base(pid, src), name(ext_name), kind(NameToKind(ext_name)) {}
+Enable::Enable(ProgramID pid, const Source& src, Extension ext) : Base(pid, src), extension(ext) {}
 
 Enable::Enable(Enable&&) = default;
 
@@ -59,6 +29,6 @@
 
 const Enable* Enable::Clone(CloneContext* ctx) const {
     auto src = ctx->Clone(source);
-    return ctx->dst->create<Enable>(src, name);
+    return ctx->dst->create<Enable>(src, extension);
 }
 }  // namespace tint::ast
diff --git a/src/tint/ast/enable.h b/src/tint/ast/enable.h
index 7bcd20e..674d9cb 100644
--- a/src/tint/ast/enable.h
+++ b/src/tint/ast/enable.h
@@ -16,57 +16,26 @@
 #define SRC_TINT_AST_ENABLE_H_
 
 #include <string>
-#include <unordered_set>
 #include <utility>
+#include <vector>
 
-#include "src/tint/ast/access.h"
-#include "src/tint/ast/expression.h"
+#include "src/tint/ast/extension.h"
+#include "src/tint/ast/node.h"
 
 namespace tint::ast {
 
-/// An instance of this class represents one extension mentioned in a
-/// "enable" derictive. Example:
-///       // Enable an extension named "f16"
-///       enable f16;
-class Enable : public Castable<Enable, Node> {
+/// An "enable" directive. Example:
+/// ```
+///   // Enable an extension named "f16"
+///   enable f16;
+/// ```
+class Enable final : public Castable<Enable, Node> {
   public:
-    ///  The enum class identifing each supported WGSL extension
-    enum class ExtensionKind {
-        /// An extension for the experimental feature
-        /// "chromium_experimental_dp4a".
-        /// See crbug.com/tint/1497 for more details
-        kChromiumExperimentalDP4a,
-
-        /// An internal reserved extension for test, named
-        /// "InternalExtensionForTesting"
-        kInternalExtensionForTesting = -2,
-        kNotAnExtension = -1,
-    };
-
-    /// Convert a string of extension name into one of ExtensionKind enum value,
-    /// the result will be ExtensionKind::kNotAnExtension if the name is not a
-    /// known extension name. A extension node of kind kNotAnExtension must not
-    /// exist in the AST tree, and using a unknown extension name in WGSL code
-    /// should result in a shader-creation error.
-    /// @param name string of the extension name
-    /// @return the ExtensionKind enum value for the extension of given name, or
-    /// kNotAnExtension if no known extension has the given name
-    static ExtensionKind NameToKind(const std::string& name);
-
-    /// Convert the ExtensionKind enum value to corresponding extension name
-    /// string. If the given enum value is kNotAnExtension or don't have a known
-    /// name, return an empty string instead.
-    /// @param kind the ExtensionKind enum value
-    /// @return string of the extension name corresponding to the given kind, or
-    /// an empty string if the given enum value is kNotAnExtension or don't have a
-    /// known corresponding name
-    static std::string KindToName(ExtensionKind kind);
-
     /// Create a extension
     /// @param pid the identifier of the program that owns this node
     /// @param src the source of this node
-    /// @param name the name of extension
-    Enable(ProgramID pid, const Source& src, const std::string& name);
+    /// @param ext the extension
+    Enable(ProgramID pid, const Source& src, Extension ext);
     /// Move constructor
     Enable(Enable&&);
 
@@ -79,14 +48,11 @@
     const Enable* Clone(CloneContext* ctx) const override;
 
     /// The extension name
-    const std::string name;
-
-    /// The extension kind
-    const ExtensionKind kind;
+    const Extension extension;
 };
 
-///  A set of extension kinds
-using ExtensionSet = std::unordered_set<Enable::ExtensionKind>;
+/// A list of enables
+using EnableList = std::vector<const Enable*>;
 
 }  // namespace tint::ast
 
diff --git a/src/tint/ast/enable_test.cc b/src/tint/ast/enable_test.cc
index 9fde780..e8b6e5c 100644
--- a/src/tint/ast/enable_test.cc
+++ b/src/tint/ast/enable_test.cc
@@ -19,40 +19,15 @@
 namespace tint::ast {
 namespace {
 
-using AstExtensionTest = TestHelper;
+using EnableTest = TestHelper;
 
-TEST_F(AstExtensionTest, Creation) {
-    auto* ext =
-        create<Enable>(Source{Source::Range{Source::Location{20, 2}, Source::Location{20, 5}}},
-                       "InternalExtensionForTesting");
+TEST_F(EnableTest, Creation) {
+    auto* ext = create<ast::Enable>(Source{{{20, 2}, {20, 5}}}, Extension::kF16);
     EXPECT_EQ(ext->source.range.begin.line, 20u);
     EXPECT_EQ(ext->source.range.begin.column, 2u);
     EXPECT_EQ(ext->source.range.end.line, 20u);
     EXPECT_EQ(ext->source.range.end.column, 5u);
-    EXPECT_EQ(ext->kind, ast::Enable::ExtensionKind::kInternalExtensionForTesting);
-}
-
-TEST_F(AstExtensionTest, Creation_InvalidName) {
-    auto* ext = create<Enable>(
-        Source{Source::Range{Source::Location{20, 2}, Source::Location{20, 5}}}, std::string());
-    EXPECT_EQ(ext->source.range.begin.line, 20u);
-    EXPECT_EQ(ext->source.range.begin.column, 2u);
-    EXPECT_EQ(ext->source.range.end.line, 20u);
-    EXPECT_EQ(ext->source.range.end.column, 5u);
-    EXPECT_EQ(ext->kind, ast::Enable::ExtensionKind::kNotAnExtension);
-}
-
-TEST_F(AstExtensionTest, NameToKind_InvalidName) {
-    EXPECT_EQ(ast::Enable::NameToKind(std::string()), ast::Enable::ExtensionKind::kNotAnExtension);
-    EXPECT_EQ(ast::Enable::NameToKind("__ImpossibleExtensionName"),
-              ast::Enable::ExtensionKind::kNotAnExtension);
-    EXPECT_EQ(ast::Enable::NameToKind("123"), ast::Enable::ExtensionKind::kNotAnExtension);
-}
-
-TEST_F(AstExtensionTest, KindToName) {
-    EXPECT_EQ(ast::Enable::KindToName(ast::Enable::ExtensionKind::kInternalExtensionForTesting),
-              "InternalExtensionForTesting");
-    EXPECT_EQ(ast::Enable::KindToName(ast::Enable::ExtensionKind::kNotAnExtension), std::string());
+    EXPECT_EQ(ext->extension, Extension::kF16);
 }
 
 }  // namespace
diff --git a/src/tint/ast/extension.cc b/src/tint/ast/extension.cc
new file mode 100644
index 0000000..f03e3a0
--- /dev/null
+++ b/src/tint/ast/extension.cc
@@ -0,0 +1,51 @@
+// 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/extension.h"
+
+namespace tint::ast {
+
+Extension ParseExtension(const std::string& name) {
+    if (name == "chromium_experimental_dp4a") {
+        return Extension::kChromiumExperimentalDP4a;
+    }
+    if (name == "chromium_disable_uniformity_analysis") {
+        return Extension::kChromiumDisableUniformityAnalysis;
+    }
+    if (name == "f16") {
+        return Extension::kF16;
+    }
+    return Extension::kNone;
+}
+
+const char* str(Extension ext) {
+    switch (ext) {
+        case Extension::kChromiumExperimentalDP4a:
+            return "chromium_experimental_dp4a";
+        case Extension::kChromiumDisableUniformityAnalysis:
+            return "chromium_disable_uniformity_analysis";
+        case Extension::kF16:
+            return "f16";
+        case Extension::kNone:
+            return "<none>";
+    }
+    return "<unknown>";
+}
+
+std::ostream& operator<<(std::ostream& out, Extension i) {
+    out << str(i);
+    return out;
+}
+
+}  // namespace tint::ast
diff --git a/src/tint/ast/extension.h b/src/tint/ast/extension.h
new file mode 100644
index 0000000..21e9ac1
--- /dev/null
+++ b/src/tint/ast/extension.h
@@ -0,0 +1,68 @@
+// 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_EXTENSION_H_
+#define SRC_TINT_AST_EXTENSION_H_
+
+#include <sstream>
+#include <string>
+
+#include "src/tint/utils/unique_vector.h"
+
+namespace tint::ast {
+
+/// An enumerator of WGSL extensions
+enum class Extension {
+    /// WGSL Extension "f16"
+    kF16,
+
+    /// An extension for the experimental feature
+    /// "chromium_experimental_dp4a".
+    /// See crbug.com/tint/1497 for more details
+    kChromiumExperimentalDP4a,
+    /// A Chromium-specific extension for disabling uniformity analysis.
+    kChromiumDisableUniformityAnalysis,
+
+    /// Reserved for representing "No extension required" or "Not a valid extension".
+    kNone,
+};
+
+/// Convert a string of extension name into one of Extension enum value, the result will be
+/// Extension::kNone if the name is not a known extension name. A extension node of kind
+/// kNone must not exist in the AST tree, and using a unknown extension name in WGSL code
+/// should result in a shader-creation error.
+/// @param name string of the extension name
+/// @return the Extension enum value for the extension of given name, or kNone if no known extension
+/// has the given name
+Extension ParseExtension(const std::string& name);
+
+/// Convert the Extension enum value to corresponding extension name string.
+/// @param ext the Extension enum value
+/// @return string of the extension name corresponding to the given kind, or
+/// an empty string if the given enum value is kNone or don't have a
+/// known corresponding name
+const char* ExtensionName(Extension ext);
+
+/// @returns the name of the extension.
+const char* str(Extension i);
+
+/// Emits the name of the extension type.
+std::ostream& operator<<(std::ostream& out, Extension i);
+
+// A unique vector of extensions
+using Extensions = utils::UniqueVector<Extension>;
+
+}  // namespace tint::ast
+
+#endif  // SRC_TINT_AST_EXTENSION_H_
diff --git a/src/tint/ast/extension_test.cc b/src/tint/ast/extension_test.cc
new file mode 100644
index 0000000..ed27674
--- /dev/null
+++ b/src/tint/ast/extension_test.cc
@@ -0,0 +1,36 @@
+
+// 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/ast/extension.h"
+
+#include "gtest/gtest.h"
+
+namespace tint::ast {
+namespace {
+
+TEST(ExtensionTest, NameToKind_InvalidName) {
+    EXPECT_EQ(ParseExtension("f16"), Extension::kF16);
+    EXPECT_EQ(ParseExtension(""), Extension::kNone);
+    EXPECT_EQ(ParseExtension("__ImpossibleExtensionName"), Extension::kNone);
+    EXPECT_EQ(ParseExtension("123"), Extension::kNone);
+}
+
+TEST(ExtensionTest, KindToName) {
+    EXPECT_EQ(std::string(str(Extension::kF16)), "f16");
+    EXPECT_EQ(std::string(str(Extension::kNone)), "<none>");
+}
+
+}  // namespace
+}  // namespace tint::ast
diff --git a/src/tint/ast/f16.cc b/src/tint/ast/f16.cc
new file mode 100644
index 0000000..0eb1be5
--- /dev/null
+++ b/src/tint/ast/f16.cc
@@ -0,0 +1,38 @@
+// 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/f16.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::F16);
+
+namespace tint::ast {
+
+F16::F16(ProgramID pid, const Source& src) : Base(pid, src) {}
+
+F16::F16(F16&&) = default;
+
+F16::~F16() = default;
+
+std::string F16::FriendlyName(const SymbolTable&) const {
+    return "f16";
+}
+
+const F16* F16::Clone(CloneContext* ctx) const {
+    auto src = ctx->Clone(source);
+    return ctx->dst->create<F16>(src);
+}
+
+}  // namespace tint::ast
diff --git a/src/tint/ast/f16.h b/src/tint/ast/f16.h
new file mode 100644
index 0000000..1b84f09
--- /dev/null
+++ b/src/tint/ast/f16.h
@@ -0,0 +1,48 @@
+// 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_F16_H_
+#define SRC_TINT_AST_F16_H_
+
+#include <string>
+
+#include "src/tint/ast/type.h"
+
+namespace tint::ast {
+
+/// A float 16 type
+class F16 : public Castable<F16, Type> {
+  public:
+    /// Constructor
+    /// @param pid the identifier of the program that owns this node
+    /// @param src the source of this node
+    F16(ProgramID pid, const Source& src);
+    /// Move constructor
+    F16(F16&&);
+    ~F16() override;
+
+    /// @param symbols the program's symbol table
+    /// @returns the name for this type that closely resembles how it would be
+    /// declared in WGSL.
+    std::string FriendlyName(const SymbolTable& symbols) const override;
+
+    /// Clones this type and all transitive types using the `CloneContext` `ctx`.
+    /// @param ctx the clone context
+    /// @return the newly cloned type
+    const F16* Clone(CloneContext* ctx) const override;
+};
+
+}  // namespace tint::ast
+
+#endif  // SRC_TINT_AST_F16_H_
diff --git a/src/tint/ast/f16_test.cc b/src/tint/ast/f16_test.cc
new file mode 100644
index 0000000..48ab284
--- /dev/null
+++ b/src/tint/ast/f16_test.cc
@@ -0,0 +1,30 @@
+// 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/f16.h"
+
+#include "src/tint/ast/test_helper.h"
+
+namespace tint::ast {
+namespace {
+
+using AstF16Test = TestHelper;
+
+TEST_F(AstF16Test, FriendlyName) {
+    auto* f = create<F16>();
+    EXPECT_EQ(f->FriendlyName(Symbols()), "f16");
+}
+
+}  // namespace
+}  // namespace tint::ast
diff --git a/src/tint/ast/float_literal_expression.h b/src/tint/ast/float_literal_expression.h
index 321efc891..72a395f 100644
--- a/src/tint/ast/float_literal_expression.h
+++ b/src/tint/ast/float_literal_expression.h
@@ -30,6 +30,8 @@
         kNone,
         /// 'f' suffix (f32)
         kF,
+        /// 'h' suffix (f16)
+        kH,
     };
 
     /// Constructor
diff --git a/src/tint/ast/module.cc b/src/tint/ast/module.cc
index e163c19..40dff98 100644
--- a/src/tint/ast/module.cc
+++ b/src/tint/ast/module.cc
@@ -68,18 +68,18 @@
             TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, var, program_id);
             global_variables_.push_back(var);
         },
-        [&](const Enable* ext) {
-            TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, ext, program_id);
-            extensions_.insert(ext->kind);
+        [&](const Enable* enable) {
+            TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, enable, program_id);
+            enables_.push_back(enable);
         },
         [&](Default) { TINT_ICE(AST, diags) << "Unknown global declaration type"; });
 }
 
-void Module::AddEnable(const ast::Enable* ext) {
-    TINT_ASSERT(AST, ext);
-    TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, ext, program_id);
-    global_declarations_.push_back(ext);
-    extensions_.insert(ext->kind);
+void Module::AddEnable(const ast::Enable* enable) {
+    TINT_ASSERT(AST, enable);
+    TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, enable, program_id);
+    global_declarations_.push_back(enable);
+    enables_.push_back(enable);
 }
 
 void Module::AddGlobalVariable(const ast::Variable* var) {
@@ -117,7 +117,7 @@
     type_decls_.clear();
     functions_.clear();
     global_variables_.clear();
-    extensions_.clear();
+    enables_.clear();
 
     for (auto* decl : global_declarations_) {
         if (!decl) {
diff --git a/src/tint/ast/module.h b/src/tint/ast/module.h
index d8be2ed..45b1ec6 100644
--- a/src/tint/ast/module.h
+++ b/src/tint/ast/module.h
@@ -78,7 +78,7 @@
     VariableList& GlobalVariables() { return global_variables_; }
 
     /// @returns the extension set for the module
-    const ExtensionSet& Extensions() const { return extensions_; }
+    const EnableList& Enables() const { return enables_; }
 
     /// Adds a type declaration to the Builder.
     /// @param decl the type declaration to add
@@ -120,7 +120,7 @@
     std::vector<const TypeDecl*> type_decls_;
     FunctionList functions_;
     VariableList global_variables_;
-    ExtensionSet extensions_;
+    EnableList enables_;
 };
 
 }  // namespace tint::ast
diff --git a/src/tint/ast/traverse_expressions.h b/src/tint/ast/traverse_expressions.h
index 650273b..59b00e9 100644
--- a/src/tint/ast/traverse_expressions.h
+++ b/src/tint/ast/traverse_expressions.h
@@ -54,40 +54,59 @@
 /// @param root the root expression node
 /// @param diags the diagnostics used for error messages
 /// @param callback the callback function. Must be of the signature:
-///        `TraverseAction(const T*)` where T is an ast::Expression type.
+///        `TraverseAction(const T* expr)` or `TraverseAction(const T* expr, size_t depth)` where T
+///        is an ast::Expression type.
 /// @return true on success, false on error
 template <TraverseOrder ORDER = TraverseOrder::LeftToRight, typename CALLBACK>
 bool TraverseExpressions(const ast::Expression* root, diag::List& diags, CALLBACK&& callback) {
     using EXPR_TYPE = std::remove_pointer_t<traits::ParameterType<CALLBACK, 0>>;
-    std::vector<const ast::Expression*> to_visit{root};
+    constexpr static bool kHasDepthArg = traits::SignatureOfT<CALLBACK>::parameter_count == 2;
 
-    auto push_pair = [&](const ast::Expression* left, const ast::Expression* right) {
+    struct Pending {
+        const ast::Expression* expr;
+        size_t depth;
+    };
+
+    std::vector<Pending> to_visit{{root, 0}};
+
+    auto push_single = [&](const ast::Expression* expr, size_t depth) {
+        to_visit.push_back({expr, depth});
+    };
+    auto push_pair = [&](const ast::Expression* left, const ast::Expression* right, size_t depth) {
         if (ORDER == TraverseOrder::LeftToRight) {
-            to_visit.push_back(right);
-            to_visit.push_back(left);
+            to_visit.push_back({right, depth});
+            to_visit.push_back({left, depth});
         } else {
-            to_visit.push_back(left);
-            to_visit.push_back(right);
+            to_visit.push_back({left, depth});
+            to_visit.push_back({right, depth});
         }
     };
-    auto push_list = [&](const std::vector<const ast::Expression*>& exprs) {
+    auto push_list = [&](const std::vector<const ast::Expression*>& exprs, size_t depth) {
         if (ORDER == TraverseOrder::LeftToRight) {
             for (auto* expr : utils::Reverse(exprs)) {
-                to_visit.push_back(expr);
+                to_visit.push_back({expr, depth});
             }
         } else {
             for (auto* expr : exprs) {
-                to_visit.push_back(expr);
+                to_visit.push_back({expr, depth});
             }
         }
     };
 
     while (!to_visit.empty()) {
-        auto* expr = to_visit.back();
+        auto p = to_visit.back();
         to_visit.pop_back();
+        const ast::Expression* expr = p.expr;
 
-        if (auto* filtered = expr->As<EXPR_TYPE>()) {
-            switch (callback(filtered)) {
+        if (auto* filtered = expr->template As<EXPR_TYPE>()) {
+            TraverseAction result;
+            if constexpr (kHasDepthArg) {
+                result = callback(filtered, p.depth);
+            } else {
+                result = callback(filtered);
+            }
+
+            switch (result) {
                 case TraverseAction::Stop:
                     return true;
                 case TraverseAction::Skip:
@@ -100,32 +119,31 @@
         bool ok = Switch(
             expr,
             [&](const IndexAccessorExpression* idx) {
-                push_pair(idx->object, idx->index);
+                push_pair(idx->object, idx->index, p.depth + 1);
                 return true;
             },
             [&](const BinaryExpression* bin_op) {
-                push_pair(bin_op->lhs, bin_op->rhs);
+                push_pair(bin_op->lhs, bin_op->rhs, p.depth + 1);
                 return true;
             },
             [&](const BitcastExpression* bitcast) {
-                to_visit.push_back(bitcast->expr);
+                push_single(bitcast->expr, p.depth + 1);
                 return true;
             },
             [&](const CallExpression* call) {
                 // TODO(crbug.com/tint/1257): Resolver breaks if we actually include
-                // the function name in the traversal. to_visit.push_back(call->func);
-                push_list(call->args);
+                // the function name in the traversal. push_single(call->func);
+                push_list(call->args, p.depth + 1);
                 return true;
             },
             [&](const MemberAccessorExpression* member) {
                 // TODO(crbug.com/tint/1257): Resolver breaks if we actually include
-                // the member name in the traversal. push_pair(member->structure,
-                // member->member);
-                to_visit.push_back(member->structure);
+                // the member name in the traversal. push_pair(member->member, p.depth + 1);
+                push_single(member->structure, p.depth + 1);
                 return true;
             },
             [&](const UnaryOpExpression* unary) {
-                to_visit.push_back(unary->expr);
+                push_single(unary->expr, p.depth + 1);
                 return true;
             },
             [&](Default) {
diff --git a/src/tint/ast/traverse_expressions_test.cc b/src/tint/ast/traverse_expressions_test.cc
index 622a7ff..e79decb 100644
--- a/src/tint/ast/traverse_expressions_test.cc
+++ b/src/tint/ast/traverse_expressions_test.cc
@@ -73,6 +73,23 @@
     }
 }
 
+TEST_F(TraverseExpressionsTest, Depth) {
+    std::vector<const ast::Expression*> e = {Expr(1_i), Expr(1_i), Expr(1_i), Expr(1_i)};
+    std::vector<const ast::Expression*> i = {Add(e[0], e[1]), Sub(e[2], e[3])};
+    auto* root = Mul(i[0], i[1]);
+
+    size_t j = 0;
+    size_t depths[] = {0, 1, 2, 2, 1, 2, 2};
+    {
+        TraverseExpressions<TraverseOrder::LeftToRight>(  //
+            root, Diagnostics(), [&](const ast::Expression* expr, size_t depth) {
+                (void)expr;
+                EXPECT_THAT(depth, depths[j++]);
+                return ast::TraverseAction::Descend;
+            });
+    }
+}
+
 TEST_F(TraverseExpressionsTest, DescendBitcastExpression) {
     auto* e = Expr(1_i);
     auto* b0 = Bitcast<i32>(e);
diff --git a/src/tint/cmd/main.cc b/src/tint/cmd/main.cc
index 52dfd70..b9c6d04 100644
--- a/src/tint/cmd/main.cc
+++ b/src/tint/cmd/main.cc
@@ -125,30 +125,36 @@
     (void)fmt;
 
 #if TINT_BUILD_SPV_WRITER
-    if (fmt == "spirv")
+    if (fmt == "spirv") {
         return Format::kSpirv;
-    if (fmt == "spvasm")
+    }
+    if (fmt == "spvasm") {
         return Format::kSpvAsm;
+    }
 #endif  // TINT_BUILD_SPV_WRITER
 
 #if TINT_BUILD_WGSL_WRITER
-    if (fmt == "wgsl")
+    if (fmt == "wgsl") {
         return Format::kWgsl;
+    }
 #endif  // TINT_BUILD_WGSL_WRITER
 
 #if TINT_BUILD_MSL_WRITER
-    if (fmt == "msl")
+    if (fmt == "msl") {
         return Format::kMsl;
+    }
 #endif  // TINT_BUILD_MSL_WRITER
 
 #if TINT_BUILD_HLSL_WRITER
-    if (fmt == "hlsl")
+    if (fmt == "hlsl") {
         return Format::kHlsl;
+    }
 #endif  // TINT_BUILD_HLSL_WRITER
 
 #if TINT_BUILD_GLSL_WRITER
-    if (fmt == "glsl")
+    if (fmt == "glsl") {
         return Format::kGlsl;
+    }
 #endif  // TINT_BUILD_GLSL_WRITER
 
     return Format::kNone;
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index 36fcfb7..569a104 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -19,6 +19,7 @@
 
 #include "src/tint/ast/bool_literal_expression.h"
 #include "src/tint/ast/call_expression.h"
+#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/interpolate_attribute.h"
@@ -27,10 +28,12 @@
 #include "src/tint/sem/array.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/depth_multisampled_texture.h"
+#include "src/tint/sem/f16.h"
 #include "src/tint/sem/f32.h"
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/i32.h"
 #include "src/tint/sem/matrix.h"
+#include "src/tint/sem/module.h"
 #include "src/tint/sem/multisampled_texture.h"
 #include "src/tint/sem/sampled_texture.h"
 #include "src/tint/sem/statement.h"
@@ -543,16 +546,13 @@
 }
 
 std::vector<std::string> Inspector::GetUsedExtensionNames() {
-    std::vector<std::string> result;
-
-    ast::ExtensionSet set = program_->AST().Extensions();
-    result.reserve(set.size());
-    for (auto kind : set) {
-        std::string name = ast::Enable::KindToName(kind);
-        result.push_back(name);
+    auto& extensions = program_->Sem().Module()->Extensions();
+    std::vector<std::string> out;
+    out.reserve(extensions.size());
+    for (auto ext : extensions) {
+        out.push_back(ast::str(ext));
     }
-
-    return result;
+    return out;
 }
 
 std::vector<std::pair<std::string, Source>> Inspector::GetEnableDirectives() {
@@ -562,7 +562,7 @@
     auto global_decls = program_->AST().GlobalDeclarations();
     for (auto* node : global_decls) {
         if (auto* ext = node->As<ast::Enable>()) {
-            result.push_back({ext->name, ext->source});
+            result.push_back({ast::str(ext->extension), ext->source});
         }
     }
 
@@ -777,7 +777,7 @@
             continue;
         }
 
-        auto* call = sem.Get(c);
+        auto* call = sem.Get(c)->UnwrapMaterialize()->As<sem::Call>();
         if (!call) {
             continue;
         }
diff --git a/src/tint/inspector/inspector_test.cc b/src/tint/inspector/inspector_test.cc
index 18a33b1..033f5b8 100644
--- a/src/tint/inspector/inspector_test.cc
+++ b/src/tint/inspector/inspector_test.cc
@@ -2849,7 +2849,7 @@
 // Test calling GetUsedExtensionNames on a shader with valid extension.
 TEST_F(InspectorGetUsedExtensionNamesTest, Simple) {
     std::string shader = R"(
-enable InternalExtensionForTesting;
+enable f16;
 
 @stage(fragment)
 fn main() {
@@ -2859,15 +2859,15 @@
 
     auto result = inspector.GetUsedExtensionNames();
     EXPECT_EQ(result.size(), 1u);
-    EXPECT_EQ(result[0], "InternalExtensionForTesting");
+    EXPECT_EQ(result[0], "f16");
 }
 
 // Test calling GetUsedExtensionNames on a shader with a extension enabled for
 // multiple times.
 TEST_F(InspectorGetUsedExtensionNamesTest, Duplicated) {
     std::string shader = R"(
-enable InternalExtensionForTesting;
-enable InternalExtensionForTesting;
+enable f16;
+enable f16;
 
 @stage(fragment)
 fn main() {
@@ -2877,7 +2877,7 @@
 
     auto result = inspector.GetUsedExtensionNames();
     EXPECT_EQ(result.size(), 1u);
-    EXPECT_EQ(result[0], "InternalExtensionForTesting");
+    EXPECT_EQ(result[0], "f16");
 }
 
 // Test calling GetEnableDirectives on a empty shader.
@@ -2906,7 +2906,7 @@
 // Test calling GetEnableDirectives on a shader with valid extension.
 TEST_F(InspectorGetEnableDirectivesTest, Simple) {
     std::string shader = R"(
-enable InternalExtensionForTesting;
+enable f16;
 
 @stage(fragment)
 fn main() {
@@ -2916,17 +2916,17 @@
 
     auto result = inspector.GetEnableDirectives();
     EXPECT_EQ(result.size(), 1u);
-    EXPECT_EQ(result[0].first, "InternalExtensionForTesting");
-    EXPECT_EQ(result[0].second.range, (Source::Range{{2, 8}, {2, 35}}));
+    EXPECT_EQ(result[0].first, "f16");
+    EXPECT_EQ(result[0].second.range, (Source::Range{{2, 8}, {2, 11}}));
 }
 
 // Test calling GetEnableDirectives on a shader with a extension enabled for
 // multiple times.
 TEST_F(InspectorGetEnableDirectivesTest, Duplicated) {
     std::string shader = R"(
-enable InternalExtensionForTesting;
+enable f16;
 
-enable InternalExtensionForTesting;
+enable f16;
 @stage(fragment)
 fn main() {
 })";
@@ -2935,10 +2935,10 @@
 
     auto result = inspector.GetEnableDirectives();
     EXPECT_EQ(result.size(), 2u);
-    EXPECT_EQ(result[0].first, "InternalExtensionForTesting");
-    EXPECT_EQ(result[0].second.range, (Source::Range{{2, 8}, {2, 35}}));
-    EXPECT_EQ(result[1].first, "InternalExtensionForTesting");
-    EXPECT_EQ(result[1].second.range, (Source::Range{{4, 8}, {4, 35}}));
+    EXPECT_EQ(result[0].first, "f16");
+    EXPECT_EQ(result[0].second.range, (Source::Range{{2, 8}, {2, 11}}));
+    EXPECT_EQ(result[1].first, "f16");
+    EXPECT_EQ(result[1].second.range, (Source::Range{{4, 8}, {4, 11}}));
 }
 
 // Crash was occuring in ::GenerateSamplerTargets, when
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 0bebe4b..bc4bf28 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -62,16 +62,32 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 // WGSL primitive types                                                       //
+// Types may be decorated with [[precedence(N)]] to prioritize which type     //
+// will be picked when multiple types of a matcher match.                     //
+// This is used to ensure that abstract numerical types materialize to the    //
+// concrete type with the lowest conversion rank.                             //
+// Types with higher the precedence values will be matched first.             //
 ////////////////////////////////////////////////////////////////////////////////
 
 // https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
 type bool
-type f32
-type i32
-type u32
+[[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
 type vec2<T>
 type vec3<T>
 type vec4<T>
+type mat2x2<T>
+type mat2x3<T>
+type mat2x4<T>
+type mat3x2<T>
+type mat3x3<T>
+type mat3x4<T>
+type mat4x2<T>
+type mat4x3<T>
+type mat4x4<T>
 [[display("vec{N}<{T}>")]]     type vec<N: num, T>
 [[display("mat{N}x{M}<{T}>")]] type mat<N: num, M: num, T>
 type ptr<S: storage_class, T, A: access>
@@ -112,6 +128,12 @@
 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 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
 
 ////////////////////////////////////////////////////////////////////////////////
 // Enum matchers                                                              //
@@ -141,7 +163,7 @@
 // functions supported by the WGSL language. This builtin definition          //
 // language supports simple static-type function declarations, as well as     //
 // single overload declarations that can match a number of different          //
-// argument types via the use of 'open-types' and 'open-numbers'.             //
+// argument types via the use of template types and template numbers          //
 //                                                                            //
 // * Basic example:                                                           //
 //                                                                            //
@@ -150,10 +172,9 @@
 //   Declares an overload of the function 'isInf' that accepts a single       //
 //   parameter of type 'f32' and returns a 'bool'.                            //
 //                                                                            //
-// An 'open-type' can be thought as a template type that is determined by the //
-// arguments to the builtin.                                                  //
+// A template type is a type determined by the arguments to the builtin.      //
 //                                                                            //
-// * Open-type example without constraint:                                    //
+// * Template type example without constraint:                                //
 //                                                                            //
 //    fn arrayLength<T>(array<T>) -> u32                                      //
 //                                                                            //
@@ -162,7 +183,7 @@
 //    element type. This overload will always return a value of the same type //
 //    as its single argument.                                                 //
 //                                                                            //
-// * Open-type example with constraint:                                       //
+// * Template type example with constraint:                                   //
 //                                                                            //
 //    fn abs<T: fiu32>(T) -> T                                                //
 //                                                                            //
@@ -170,10 +191,10 @@
 //    argument of type 'f32', 'i32' or 'u32', which returns a value of the    //
 //    same argument type.                                                     //
 //                                                                            //
-// Similarly an 'open-number' can be thought as a template number or          //
-// enumerator that is determined by the arguments to the builtin.             //
+// Similarly a template number is a number or enumerator that is determined   //
+// by the arguments to the builtin.                                           //
 //                                                                            //
-// * Open-number example:                                                     //
+// * Template number example:                                                 //
 //                                                                            //
 //    fn dpdx<N: num>(vec<N, f32>) -> vec<N, f32>                             //
 //                                                                            //
@@ -182,54 +203,76 @@
 //    the same argument type.                                                 //
 //                                                                            //
 //                                                                            //
-// Matching algorithm:                                                        //
-// -------------------                                                        //
+// Matching algorithm for a single overload:                                  //
+// -----------------------------------------                                  //
 //                                                                            //
-// Prior to matching an overload, all open-types are undefined.               //
+// The goal of matching is to compare a function call's arguments in the      //
+// program source against a possibly-templated overload declaration, and      //
+// determine if the call satisfies the form and type constraints of the       //
+// overload. Currently it is impossible for a call to match more than one     //
+// overload definition. In the event that more than one overload matches, an  //
+// ICE will be raised. Note that Tint may need to support multiple-overload   //
+// resolution in the future, depending on future overload definitions.        //
 //                                                                            //
-// Open-types become closed-types (pinned to a fixed type) on the first       //
-// attempt to match an argument to that open-type.                            //
-// Once open-types are closed, they remain that type for the rest of the      //
-// overload evaluation.                                                       //
+// Prior to matching an overload, all template types are undefined.           //
+//                                                                            //
+// Template types are first defined with the type of the leftmost argument    //
+// that matches against that template type name. Subsequent arguments that    //
+// attempt to match against the template type name will either reject the     //
+// overload or refine the template, in one of 3 ways:                         //
+// (a) Fail to match, causing the overload to be immediately rejected.        //
+// (b) Match the existing template type, either exactly or via implicit       //
+//     conversion, and overload resolution continues.                         //
+// (c) Match via implicit conversion of the currently defined template type   //
+//     to the argument type. In this situation, the template type is refined  //
+//     with the more constrained argument type, and overload resolution       //
+//     continues.                                                             //
 //                                                                            //
 // To better understand, let's consider the following hypothetical overload   //
 // declaration:                                                               //
 //                                                                            //
 //    fn foo<T: scalar>(T, T);                                                //
 //                                                                            //
-//    T           - is the open-type                                          //
+//    T           - is the template type name                                 //
 //    scalar      - is a matcher for the types 'f32', 'i32', 'u32' or 'bool'  //
 //                  (declared above)                                          //
-//    <T: scalar> - declares the open-type T, with the constraint that T must //
-//                  match one of 'f32', 'i32', 'u32' or 'bool'.               //
+//    <T: scalar> - declares the template type T, with the constraint that T  //
+//                  must match one of 'f32', 'i32', 'u32' or 'bool'.          //
 //                                                                            //
 // The process for resolving this overload is as follows:                     //
 //                                                                            //
 //   (1) The overload resolver begins by attempting to match the argument     //
 //       types from left to right.                                            //
-//       The first parameter type is compared against the argument type.      //
-//       As the open-type T has not been closed yet, T is closed as the type  //
-//       of the first argument.                                               //
+//       The first parameter type is compared against the argument type T.    //
+//       As the template type T has not been defined yet, T is defined as the //
+//       type of the first argument.                                          //
 //       There's no verification that the T type is a scalar at this stage.   //
 //   (2) The second parameter is then compared against the second argument.   //
-//       As the open-type T is now closed, the argument type is compared      //
-//       against the value of the closed-type of T. If the types match, then  //
-//       the overload is still a candidate for matching, otherwise the        //
-//       overload is no longer considered.                                    //
-//   (3) If all the parameters matched, constraints on the open-types need    //
-//       to be checked next. If the closed-type does not match the 'match'    //
-//       constraint, then the overload is no longer considered.               //
+//       As the template type T is now defined the argument type is compared  //
+//       against the value of the defined type of T. Depending on the         //
+//       comparison of the argument type to the template type, either the     //
+//       actions of (a), (b) or (c) from above will occur.                    //
+//   (3) If all the parameters matched, constraints on the template types     //
+//       need to be checked next. If the defined type does not match the      //
+//       'match' constraint, then the overload is no longer considered.       //
 //                                                                            //
-// The algorithm for matching open-numbers is almost identical to open-types, //
-// except of course, they match against integer numbers or enumerators        //
-// instead of types.                                                          //
+// This algorithm is less general than the overload resolution described in   //
+// the WGSL spec.  But it makes the same decisions because the overloads      //
+// defined by WGSL are monotonic in the sense that once a template parameter  //
+// has been refined, there is never a need to backtrack and un-refine it to   //
+// match a later argument.                                                    //
+//                                                                            //
+// The algorithm for matching template numbers is similar to matching         //
+// template types, except numbers need to exactly match across all uses -     //
+// there is no implicit conversion. Template numbers may match integer        //
+// numbers or enumerators.                                                    //
 //                                                                            //
 //                                                                            //
 // * More examples:                                                           //
 //                                                                            //
 //   fn F()                                                                   //
 //     - Function called F.                                                   //
-//       No open types or numbers, no parameters, no return value             //
+//       No template types or numbers, no parameters, no return value         //
 //                                                                            //
 //   fn F() -> RETURN_TYPE                                                    //
 //     - Function with RETURN_TYPE as the return type value                   //
@@ -243,21 +286,21 @@
 //       some builtin functions                                               //
 //                                                                            //
 //   fn F<T>(T)                                                               //
-//     - Single parameter of unconstrained open-type T (any type)             //
+//     - Single parameter of unconstrained template type T (any type)         //
 //                                                                            //
 //   fn F<T: scalar>(T)                                                       //
-//     - Single parameter of constrained open-type T (must be a scalar)       //
+//     - Single parameter of constrained template type T (must be a scalar)   //
 //                                                                            //
 //   fn F<T: fiu32>(T) -> T                                                   //
-//     - Single parameter of constrained open-type T (must be a one of fiu32) //
-//       Return type matches parameter type                                   //
+//     - Single parameter of constrained template type T (must be a one of    //
+//       fiu32) Return type matches parameter type                            //
 //                                                                            //
 //   fn F<T, N: num>(vec<N, T>)                                               //
-//     - Single parameter of vector type with open-number size N and element  //
-//       open-type T                                                          //
+//     - Single parameter of vector type with template number size N and      //
+//       element template type T                                              //
 //                                                                            //
 //   fn F<A: access>(texture_storage_1d<f32_texel_format, A>)                 //
-//     - Single parameter of texture_storage_1d type with open-number         //
+//     - Single parameter of texture_storage_1d type with template number     //
 //       access-control C, and of a texel format that is listed in            //
 //       f32_texel_format                                                     //
 //                                                                            //
@@ -563,6 +606,140 @@
 [[stage("fragment", "compute")]] fn atomicCompareExchangeWeak<T: iu32, S: workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T, T) -> vec2<T>
 
 ////////////////////////////////////////////////////////////////////////////////
+// Type constructors                                                          //
+////////////////////////////////////////////////////////////////////////////////
+
+// Zero value constructors
+ctor i32() -> i32
+ctor u32() -> u32
+ctor f32() -> f32
+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>
+
+// Identity constructors
+ctor i32(i32) -> i32
+ctor u32(u32) -> u32
+ctor f32(f32) -> f32
+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>
+
+// Vector constructors
+ctor vec2<T: abstract_or_scalar>(T) -> vec2<T>
+ctor vec2<T: abstract_or_scalar>(x: T, y: T) -> vec2<T>
+ctor vec3<T: abstract_or_scalar>(T) -> vec3<T>
+ctor vec3<T: abstract_or_scalar>(x: T, y: T, z: T) -> vec3<T>
+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>
+ctor vec4<T: abstract_or_scalar>(T) -> vec4<T>
+ctor vec4<T: abstract_or_scalar>(x: T, y: T, z: T, w: T) -> vec4<T>
+ctor vec4<T: abstract_or_scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T>
+ctor vec4<T: abstract_or_scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T>
+ctor vec4<T: abstract_or_scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T>
+ctor vec4<T: abstract_or_scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T>
+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>
+
+// 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>
+
+////////////////////////////////////////////////////////////////////////////////
+// Type conversions                                                           //
+////////////////////////////////////////////////////////////////////////////////
+conv f32<T: scalar_no_f32>(T) -> f32
+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: 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: 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: 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>
+
+////////////////////////////////////////////////////////////////////////////////
 // Operators                                                                  //
 //                                                                            //
 // The operator declarations below declare all the unary and binary operators //
diff --git a/src/tint/number.h b/src/tint/number.h
index a4bd369..c3be236 100644
--- a/src/tint/number.h
+++ b/src/tint/number.h
@@ -18,6 +18,12 @@
 #include <stdint.h>
 #include <functional>
 
+namespace tint::detail {
+/// An empty structure used as a unique template type for Number when
+/// specializing for the f16 type.
+struct NumberKindF16 {};
+}  // namespace tint::detail
+
 namespace tint {
 
 /// Number wraps a integer or floating point number, enforcing explicit casting.
@@ -72,6 +78,43 @@
     return Number<A>(a) == b;
 }
 
+/// The partial specification of Number for f16 type, storing the f16 value as float,
+/// and enforcing proper explicit casting.
+template <>
+struct Number<detail::NumberKindF16> {
+    /// Constructor. The value is zero-initialized.
+    Number() = default;
+
+    /// Constructor.
+    /// @param v the value to initialize this Number to
+    template <typename U>
+    explicit Number(U v) : value(static_cast<float>(v)) {}
+
+    /// Constructor.
+    /// @param v the value to initialize this Number to
+    template <typename U>
+    explicit Number(Number<U> v) : value(static_cast<float>(v.value)) {}
+
+    /// Conversion operator
+    /// @returns the value as the internal representation type of F16
+    operator float() const { return value; }
+
+    /// Negation operator
+    /// @returns the negative value of the number
+    Number operator-() const { return Number<detail::NumberKindF16>(-value); }
+
+    /// Assignment operator with parameter as native floating point type
+    /// @param v the new value
+    /// @returns this Number so calls can be chained
+    Number& operator=(float v) {
+        value = v;
+        return *this;
+    }
+
+    /// The number value, stored as float
+    float value = {};
+};
+
 /// `AInt` is a type alias to `Number<int64_t>`.
 using AInt = Number<int64_t>;
 /// `AFloat` is a type alias to `Number<double>`.
@@ -83,41 +126,54 @@
 using u32 = Number<uint32_t>;
 /// `f32` is a type alias to `Number<float>`
 using f32 = Number<float>;
+/// `f16` is a type alias to `Number<detail::NumberKindF16>`, which should be IEEE 754 binary16.
+/// However since C++ don't have native binary16 type, the value is stored as float.
+using f16 = Number<detail::NumberKindF16>;
 
 }  // namespace tint
 
 namespace tint::number_suffixes {
 
 /// Literal suffix for abstract integer literals
-inline AInt operator"" _a(unsigned long long int value) {  // NOLINT
+inline AInt operator""_a(unsigned long long int value) {  // NOLINT
     return AInt(static_cast<int64_t>(value));
 }
 
 /// Literal suffix for abstract float literals
-inline AFloat operator"" _a(long double value) {  // NOLINT
+inline AFloat operator""_a(long double value) {  // NOLINT
     return AFloat(static_cast<double>(value));
 }
 
 /// Literal suffix for i32 literals
-inline i32 operator"" _i(unsigned long long int value) {  // NOLINT
+inline i32 operator""_i(unsigned long long int value) {  // NOLINT
     return i32(static_cast<int32_t>(value));
 }
 
 /// Literal suffix for u32 literals
-inline u32 operator"" _u(unsigned long long int value) {  // NOLINT
+inline u32 operator""_u(unsigned long long int value) {  // NOLINT
     return u32(static_cast<uint32_t>(value));
 }
 
 /// Literal suffix for f32 literals
-inline f32 operator"" _f(long double value) {  // NOLINT
+inline f32 operator""_f(long double value) {  // NOLINT
     return f32(static_cast<double>(value));
 }
 
 /// Literal suffix for f32 literals
-inline f32 operator"" _f(unsigned long long int value) {  // NOLINT
+inline f32 operator""_f(unsigned long long int value) {  // NOLINT
     return f32(static_cast<double>(value));
 }
 
+/// Literal suffix for f16 literals
+inline f16 operator""_h(long double value) {  // NOLINT
+    return f16(static_cast<double>(value));
+}
+
+/// Literal suffix for f16 literals
+inline f16 operator""_h(unsigned long long int value) {  // NOLINT
+    return f16(static_cast<double>(value));
+}
+
 }  // namespace tint::number_suffixes
 
 #endif  // SRC_TINT_NUMBER_H_
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 7dd2c81..38970a5 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -39,7 +39,9 @@
 #include "src/tint/ast/disable_validation_attribute.h"
 #include "src/tint/ast/discard_statement.h"
 #include "src/tint/ast/enable.h"
+#include "src/tint/ast/extension.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/float_literal_expression.h"
@@ -82,6 +84,7 @@
 #include "src/tint/sem/bool.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/i32.h"
 #include "src/tint/sem/matrix.h"
@@ -385,6 +388,15 @@
             return builder->create<ast::Bool>(source);
         }
 
+        /// @returns a f16 type
+        const ast::F16* f16() const { return builder->create<ast::F16>(); }
+
+        /// @param source the Source of the node
+        /// @returns a f16 type
+        const ast::F16* f16(const Source& source) const {
+            return builder->create<ast::F16>(source);
+        }
+
         /// @returns a f32 type
         const ast::F32* f32() const { return builder->create<ast::F32>(); }
 
@@ -1005,6 +1017,21 @@
     }
 
     /// @param source the source information
+    /// @param value the float value
+    /// @return a 'h'-suffixed FloatLiteralExpression for the f16 value
+    const ast::FloatLiteralExpression* Expr(const Source& source, f16 value) {
+        return create<ast::FloatLiteralExpression>(source, static_cast<double>(value.value),
+                                                   ast::FloatLiteralExpression::Suffix::kH);
+    }
+
+    /// @param value the float value
+    /// @return a 'h'-suffixed FloatLiteralExpression for the f16 value
+    const ast::FloatLiteralExpression* Expr(f16 value) {
+        return create<ast::FloatLiteralExpression>(static_cast<double>(value.value),
+                                                   ast::FloatLiteralExpression::Suffix::kH);
+    }
+
+    /// @param source the source information
     /// @param value the integer value
     /// @return an unsuffixed IntLiteralExpression for the AInt value
     const ast::IntLiteralExpression* Expr(const Source& source, AInt value) {
@@ -1281,6 +1308,15 @@
         return Construct(ty.array(subtype, std::forward<EXPR>(n)), std::forward<ARGS>(args)...);
     }
 
+    /// Adds the extension to the list of enable directives at the top of the module.
+    /// @param ext the extension to enable
+    /// @return an `ast::Enable` enabling the given extension.
+    const ast::Enable* Enable(ast::Extension ext) {
+        auto* enable = create<ast::Enable>(ext);
+        AST().AddEnable(enable);
+        return enable;
+    }
+
     /// @param name the variable name
     /// @param type the variable type
     /// @param optional the optional variable settings.
@@ -2600,6 +2636,25 @@
     /// the type declaration has no resolved type.
     const sem::Type* TypeOf(const ast::TypeDecl* type_decl) const;
 
+    /// @param type a type
+    /// @returns the name for `type` that closely resembles how it would be
+    /// declared in WGSL.
+    std::string FriendlyName(const ast::Type* type) {
+        return type ? type->FriendlyName(Symbols()) : "<null>";
+    }
+
+    /// @param type a type
+    /// @returns the name for `type` that closely resembles how it would be
+    /// declared in WGSL.
+    std::string FriendlyName(const sem::Type* type) {
+        return type ? type->FriendlyName(Symbols()) : "<null>";
+    }
+
+    /// Overload of FriendlyName, which removes an ambiguity when passing nullptr.
+    /// Simplifies test code.
+    /// @returns "<null>"
+    std::string FriendlyName(std::nullptr_t) { return "<null>"; }
+
     /// Wraps the ast::Expression in a statement. This is used by tests that
     /// construct a partial AST and require the Resolver to reach these
     /// nodes.
@@ -2675,6 +2730,10 @@
     static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->f32(); }
 };
 template <>
+struct ProgramBuilder::TypesBuilder::CToAST<f16> {
+    static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->f16(); }
+};
+template <>
 struct ProgramBuilder::TypesBuilder::CToAST<bool> {
     static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->bool_(); }
 };
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 82f6224..d88773e 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -602,10 +602,12 @@
     // header, we will visit its merge block, then its continue target (if any).
     // Also records the post order ordering.
     void VisitBackward(uint32_t id) {
-        if (id == 0)
+        if (id == 0) {
             return;
-        if (visited_.count(id))
+        }
+        if (visited_.count(id)) {
             return;
+        }
         visited_.insert(id);
 
         const spvtools::opt::BasicBlock* bb = id_to_block_[id];  // non-null for valid modules
@@ -1600,8 +1602,9 @@
         bool is_single_block_loop = false;
         block_info->basic_block->ForEachSuccessorLabel(
             [&is_single_block_loop, block_id](const uint32_t succ) {
-                if (block_id == succ)
+                if (block_id == succ) {
                     is_single_block_loop = true;
+                }
             });
         const auto ct = block_info->continue_for_header;
         block_info->is_continue_entire_loop = ct == block_id;
diff --git a/src/tint/reader/spirv/parser_impl_barrier_test.cc b/src/tint/reader/spirv/parser_impl_barrier_test.cc
index 0549bd1..fdfa3bf 100644
--- a/src/tint/reader/spirv/parser_impl_barrier_test.cc
+++ b/src/tint/reader/spirv/parser_impl_barrier_test.cc
@@ -69,7 +69,7 @@
     auto* call = helper->body->statements[0]->As<ast::CallStatement>();
     ASSERT_NE(call, nullptr);
     EXPECT_EQ(call->expr->args.size(), 0u);
-    auto* sem_call = program.Sem().Get(call->expr);
+    auto* sem_call = program.Sem().Get<sem::Call>(call->expr);
     ASSERT_NE(sem_call, nullptr);
     auto* builtin = sem_call->Target()->As<sem::Builtin>();
     ASSERT_NE(builtin, nullptr);
@@ -102,7 +102,7 @@
     auto* call = helper->body->statements[0]->As<ast::CallStatement>();
     ASSERT_NE(call, nullptr);
     EXPECT_EQ(call->expr->args.size(), 0u);
-    auto* sem_call = program.Sem().Get(call->expr);
+    auto* sem_call = program.Sem().Get<sem::Call>(call->expr);
     ASSERT_NE(sem_call, nullptr);
     auto* builtin = sem_call->Target()->As<sem::Builtin>();
     ASSERT_NE(builtin, nullptr);
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index db75613..20b3364 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -232,8 +232,9 @@
 }
 
 bool Lexer::matches(size_t pos, std::string_view sub_string) {
-    if (pos >= length())
+    if (pos >= length()) {
         return false;
+    }
     return substr(pos, sub_string.size()) == sub_string;
 }
 
@@ -265,8 +266,9 @@
 
         // If the cursor didn't advance we didn't remove any blankspace
         // so we're done.
-        if (loc == location_)
+        if (loc == location_) {
             break;
+        }
     }
     if (is_eof()) {
         return {Token::Type::kEOF, begin_source()};
@@ -1043,108 +1045,159 @@
 }
 
 Token Lexer::check_keyword(const Source& source, std::string_view str) {
-    if (str == "array")
+    if (str == "array") {
         return {Token::Type::kArray, source, "array"};
-    if (str == "atomic")
+    }
+    if (str == "atomic") {
         return {Token::Type::kAtomic, source, "atomic"};
-    if (str == "bitcast")
+    }
+    if (str == "bitcast") {
         return {Token::Type::kBitcast, source, "bitcast"};
-    if (str == "bool")
+    }
+    if (str == "bool") {
         return {Token::Type::kBool, source, "bool"};
-    if (str == "break")
+    }
+    if (str == "break") {
         return {Token::Type::kBreak, source, "break"};
-    if (str == "case")
+    }
+    if (str == "case") {
         return {Token::Type::kCase, source, "case"};
-    if (str == "continue")
+    }
+    if (str == "continue") {
         return {Token::Type::kContinue, source, "continue"};
-    if (str == "continuing")
+    }
+    if (str == "continuing") {
         return {Token::Type::kContinuing, source, "continuing"};
-    if (str == "discard")
+    }
+    if (str == "discard") {
         return {Token::Type::kDiscard, source, "discard"};
-    if (str == "default")
+    }
+    if (str == "default") {
         return {Token::Type::kDefault, source, "default"};
-    if (str == "else")
+    }
+    if (str == "else") {
         return {Token::Type::kElse, source, "else"};
-    if (str == "enable")
+    }
+    if (str == "enable") {
         return {Token::Type::kEnable, source, "enable"};
-    if (str == "f32")
+    }
+    if (str == "f16") {
+        return {Token::Type::kF16, source, "f16"};
+    }
+    if (str == "f32") {
         return {Token::Type::kF32, source, "f32"};
-    if (str == "fallthrough")
+    }
+    if (str == "fallthrough") {
         return {Token::Type::kFallthrough, source, "fallthrough"};
-    if (str == "false")
+    }
+    if (str == "false") {
         return {Token::Type::kFalse, source, "false"};
-    if (str == "fn")
+    }
+    if (str == "fn") {
         return {Token::Type::kFn, source, "fn"};
-    if (str == "for")
+    }
+    if (str == "for") {
         return {Token::Type::kFor, source, "for"};
-    if (str == "function")
+    }
+    if (str == "function") {
         return {Token::Type::kFunction, source, "function"};
-    if (str == "i32")
+    }
+    if (str == "i32") {
         return {Token::Type::kI32, source, "i32"};
-    if (str == "if")
+    }
+    if (str == "if") {
         return {Token::Type::kIf, source, "if"};
-    if (str == "import")
+    }
+    if (str == "import") {
         return {Token::Type::kImport, source, "import"};
-    if (str == "let")
+    }
+    if (str == "let") {
         return {Token::Type::kLet, source, "let"};
-    if (str == "loop")
+    }
+    if (str == "loop") {
         return {Token::Type::kLoop, source, "loop"};
-    if (str == "mat2x2")
+    }
+    if (str == "mat2x2") {
         return {Token::Type::kMat2x2, source, "mat2x2"};
-    if (str == "mat2x3")
+    }
+    if (str == "mat2x3") {
         return {Token::Type::kMat2x3, source, "mat2x3"};
-    if (str == "mat2x4")
+    }
+    if (str == "mat2x4") {
         return {Token::Type::kMat2x4, source, "mat2x4"};
-    if (str == "mat3x2")
+    }
+    if (str == "mat3x2") {
         return {Token::Type::kMat3x2, source, "mat3x2"};
-    if (str == "mat3x3")
+    }
+    if (str == "mat3x3") {
         return {Token::Type::kMat3x3, source, "mat3x3"};
-    if (str == "mat3x4")
+    }
+    if (str == "mat3x4") {
         return {Token::Type::kMat3x4, source, "mat3x4"};
-    if (str == "mat4x2")
+    }
+    if (str == "mat4x2") {
         return {Token::Type::kMat4x2, source, "mat4x2"};
-    if (str == "mat4x3")
+    }
+    if (str == "mat4x3") {
         return {Token::Type::kMat4x3, source, "mat4x3"};
-    if (str == "mat4x4")
+    }
+    if (str == "mat4x4") {
         return {Token::Type::kMat4x4, source, "mat4x4"};
-    if (str == "override")
+    }
+    if (str == "override") {
         return {Token::Type::kOverride, source, "override"};
-    if (str == "private")
+    }
+    if (str == "private") {
         return {Token::Type::kPrivate, source, "private"};
-    if (str == "ptr")
+    }
+    if (str == "ptr") {
         return {Token::Type::kPtr, source, "ptr"};
-    if (str == "return")
+    }
+    if (str == "return") {
         return {Token::Type::kReturn, source, "return"};
-    if (str == "sampler")
+    }
+    if (str == "sampler") {
         return {Token::Type::kSampler, source, "sampler"};
-    if (str == "sampler_comparison")
+    }
+    if (str == "sampler_comparison") {
         return {Token::Type::kComparisonSampler, source, "sampler_comparison"};
-    if (str == "storage_buffer" || str == "storage")
+    }
+    if (str == "storage_buffer" || str == "storage") {
         return {Token::Type::kStorage, source, "storage"};
-    if (str == "struct")
+    }
+    if (str == "struct") {
         return {Token::Type::kStruct, source, "struct"};
-    if (str == "switch")
+    }
+    if (str == "switch") {
         return {Token::Type::kSwitch, source, "switch"};
-    if (str == "texture_1d")
+    }
+    if (str == "texture_1d") {
         return {Token::Type::kTextureSampled1d, source, "texture_1d"};
-    if (str == "texture_2d")
+    }
+    if (str == "texture_2d") {
         return {Token::Type::kTextureSampled2d, source, "texture_2d"};
-    if (str == "texture_2d_array")
+    }
+    if (str == "texture_2d_array") {
         return {Token::Type::kTextureSampled2dArray, source, "texture_2d_array"};
-    if (str == "texture_3d")
+    }
+    if (str == "texture_3d") {
         return {Token::Type::kTextureSampled3d, source, "texture_3d"};
-    if (str == "texture_cube")
+    }
+    if (str == "texture_cube") {
         return {Token::Type::kTextureSampledCube, source, "texture_cube"};
+    }
     if (str == "texture_cube_array") {
         return {Token::Type::kTextureSampledCubeArray, source, "texture_cube_array"};
     }
-    if (str == "texture_depth_2d")
+    if (str == "texture_depth_2d") {
         return {Token::Type::kTextureDepth2d, source, "texture_depth_2d"};
+    }
     if (str == "texture_depth_2d_array") {
         return {Token::Type::kTextureDepth2dArray, source, "texture_depth_2d_array"};
     }
-    if (str == "texture_depth_cube")
+    if (str == "texture_depth_cube") {
         return {Token::Type::kTextureDepthCube, source, "texture_depth_cube"};
+    }
     if (str == "texture_depth_cube_array") {
         return {Token::Type::kTextureDepthCubeArray, source, "texture_depth_cube_array"};
     }
@@ -1169,24 +1222,33 @@
     if (str == "texture_storage_3d") {
         return {Token::Type::kTextureStorage3d, source, "texture_storage_3d"};
     }
-    if (str == "true")
+    if (str == "true") {
         return {Token::Type::kTrue, source, "true"};
-    if (str == "type")
+    }
+    if (str == "type") {
         return {Token::Type::kType, source, "type"};
-    if (str == "u32")
+    }
+    if (str == "u32") {
         return {Token::Type::kU32, source, "u32"};
-    if (str == "uniform")
+    }
+    if (str == "uniform") {
         return {Token::Type::kUniform, source, "uniform"};
-    if (str == "var")
+    }
+    if (str == "var") {
         return {Token::Type::kVar, source, "var"};
-    if (str == "vec2")
+    }
+    if (str == "vec2") {
         return {Token::Type::kVec2, source, "vec2"};
-    if (str == "vec3")
+    }
+    if (str == "vec3") {
         return {Token::Type::kVec3, source, "vec3"};
-    if (str == "vec4")
+    }
+    if (str == "vec4") {
         return {Token::Type::kVec4, source, "vec4"};
-    if (str == "workgroup")
+    }
+    if (str == "workgroup") {
         return {Token::Type::kWorkgroup, source, "workgroup"};
+    }
     return {};
 }
 
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index b2b9a03..4ad0f76 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -124,8 +124,8 @@
 
 // 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 == "f16" ||
-           t == "f64" || t == "handle" || t == "i8" || t == "i16" || t == "i64" || t == "mat" ||
+    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";
 }
@@ -141,18 +141,24 @@
     /// @return the current enter-exit depth for the given block token type. If
     /// `t` is not a block token type, then 0 is always returned.
     int consume(const Token& t) {
-        if (t.Is(Token::Type::kBraceLeft))
+        if (t.Is(Token::Type::kBraceLeft)) {
             return brace++;
-        if (t.Is(Token::Type::kBraceRight))
+        }
+        if (t.Is(Token::Type::kBraceRight)) {
             return brace--;
-        if (t.Is(Token::Type::kBracketLeft))
+        }
+        if (t.Is(Token::Type::kBracketLeft)) {
             return bracket++;
-        if (t.Is(Token::Type::kBracketRight))
+        }
+        if (t.Is(Token::Type::kBracketRight)) {
             return bracket--;
-        if (t.Is(Token::Type::kParenLeft))
+        }
+        if (t.Is(Token::Type::kParenLeft)) {
             return paren++;
-        if (t.Is(Token::Type::kParenRight))
+        }
+        if (t.Is(Token::Type::kParenRight)) {
             return paren--;
+        }
         return 0;
     }
 };
@@ -310,6 +316,9 @@
             if (after_global_decl) {
                 add_error(p, "enable directives must come before all global declarations");
             }
+        } else if (ed.errored) {
+            // Found a invalid enable directive.
+            continue;
         } else {
             auto gd = global_decl();
 
@@ -345,6 +354,11 @@
             synchronized_ = true;
             next();
             name = {t.to_str(), t.source()};
+        } else if (t.Is(Token::Type::kF16)) {
+            // `f16` is a valid extension name and also a keyword
+            synchronized_ = true;
+            next();
+            name = {"f16", t.source()};
         } else if (handle_error(t)) {
             // The token might itself be an error.
             return Failure::kErrored;
@@ -358,13 +372,11 @@
             return Failure::kErrored;
         }
 
-        if (ast::Enable::NameToKind(name.value) != ast::Enable::ExtensionKind::kNotAnExtension) {
-            const ast::Enable* extension = create<ast::Enable>(name.source, name.value);
-            builder_.AST().AddEnable(extension);
-        } else {
-            // Error if an unknown extension is used
+        auto extension = ast::ParseExtension(name.value);
+        if (extension == ast::Extension::kNone) {
             return add_error(name.source, "unsupported extension: '" + name.value + "'");
         }
+        builder_.AST().AddEnable(create<ast::Enable>(name.source, extension));
 
         return true;
     });
@@ -387,56 +399,66 @@
 //  | struct_decl
 //  | function_decl
 Maybe<bool> ParserImpl::global_decl() {
-    if (match(Token::Type::kSemicolon) || match(Token::Type::kEOF))
+    if (match(Token::Type::kSemicolon) || match(Token::Type::kEOF)) {
         return true;
+    }
 
     bool errored = false;
 
     auto attrs = attribute_list();
-    if (attrs.errored)
+    if (attrs.errored) {
         errored = true;
-    if (!continue_parsing())
+    }
+    if (!continue_parsing()) {
         return Failure::kErrored;
+    }
 
     auto decl = sync(Token::Type::kSemicolon, [&]() -> Maybe<bool> {
         auto gv = global_variable_decl(attrs.value);
-        if (gv.errored)
+        if (gv.errored) {
             return Failure::kErrored;
+        }
         if (gv.matched) {
-            if (!expect("variable declaration", Token::Type::kSemicolon))
+            if (!expect("variable declaration", Token::Type::kSemicolon)) {
                 return Failure::kErrored;
+            }
 
             builder_.AST().AddGlobalVariable(gv.value);
             return true;
         }
 
         auto gc = global_constant_decl(attrs.value);
-        if (gc.errored)
+        if (gc.errored) {
             return Failure::kErrored;
+        }
 
         if (gc.matched) {
-            if (!expect("let declaration", Token::Type::kSemicolon))
+            if (!expect("let declaration", Token::Type::kSemicolon)) {
                 return Failure::kErrored;
+            }
 
             builder_.AST().AddGlobalVariable(gc.value);
             return true;
         }
 
         auto ta = type_alias();
-        if (ta.errored)
+        if (ta.errored) {
             return Failure::kErrored;
+        }
 
         if (ta.matched) {
-            if (!expect("type alias", Token::Type::kSemicolon))
+            if (!expect("type alias", Token::Type::kSemicolon)) {
                 return Failure::kErrored;
+            }
 
             builder_.AST().AddTypeDecl(ta.value);
             return true;
         }
 
         auto str = struct_decl();
-        if (str.errored)
+        if (str.errored) {
             return Failure::kErrored;
+        }
 
         if (str.matched) {
             builder_.AST().AddTypeDecl(str.value);
@@ -504,16 +526,19 @@
 //  | variable_attribute_list* variable_decl EQUAL const_expr
 Maybe<const ast::Variable*> ParserImpl::global_variable_decl(ast::AttributeList& attrs) {
     auto decl = variable_decl();
-    if (decl.errored)
+    if (decl.errored) {
         return Failure::kErrored;
-    if (!decl.matched)
+    }
+    if (!decl.matched) {
         return Failure::kNoMatch;
+    }
 
     const ast::Expression* constructor = nullptr;
     if (match(Token::Type::kEqual)) {
         auto expr = expect_const_expr();
-        if (expr.errored)
+        if (expr.errored) {
             return Failure::kErrored;
+        }
         constructor = expr.value;
     }
 
@@ -546,8 +571,9 @@
     }
 
     auto decl = expect_variable_ident_decl(use, /* allow_inferred = */ true);
-    if (decl.errored)
+    if (decl.errored) {
         return Failure::kErrored;
+    }
 
     const ast::Expression* initializer = nullptr;
     if (match(Token::Type::kEqual)) {
@@ -573,20 +599,23 @@
 //   : VAR variable_qualifier? variable_ident_decl
 Maybe<ParserImpl::VarDeclInfo> ParserImpl::variable_decl(bool allow_inferred) {
     Source source;
-    if (!match(Token::Type::kVar, &source))
+    if (!match(Token::Type::kVar, &source)) {
         return Failure::kNoMatch;
+    }
 
     VariableQualifier vq;
     auto explicit_vq = variable_qualifier();
-    if (explicit_vq.errored)
+    if (explicit_vq.errored) {
         return Failure::kErrored;
+    }
     if (explicit_vq.matched) {
         vq = explicit_vq.value;
     }
 
     auto decl = expect_variable_ident_decl("variable declaration", allow_inferred);
-    if (decl.errored)
+    if (decl.errored) {
         return Failure::kErrored;
+    }
 
     return VarDeclInfo{decl->source, decl->name, vq.storage_class, vq.access, decl->type};
 }
@@ -600,16 +629,19 @@
 //                         COMMA access GREATER_THAN
 Maybe<const ast::Type*> ParserImpl::texture_samplers() {
     auto type = sampler();
-    if (type.matched)
+    if (type.matched) {
         return type;
+    }
 
     type = depth_texture();
-    if (type.matched)
+    if (type.matched) {
         return type;
+    }
 
     type = external_texture();
-    if (type.matched)
+    if (type.matched) {
         return type.value;
+    }
 
     auto source_range = make_source_range();
 
@@ -618,8 +650,9 @@
         const char* use = "sampled texture type";
 
         auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
-        if (subtype.errored)
+        if (subtype.errored) {
             return Failure::kErrored;
+        }
 
         return builder_.ty.sampled_texture(source_range, dim.value, subtype.value);
     }
@@ -629,8 +662,9 @@
         const char* use = "multisampled texture type";
 
         auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
-        if (subtype.errored)
+        if (subtype.errored) {
             return Failure::kErrored;
+        }
 
         return builder_.ty.multisampled_texture(source_range, ms_dim.value, subtype.value);
     }
@@ -673,11 +707,13 @@
 //  | SAMPLER_COMPARISON
 Maybe<const ast::Type*> ParserImpl::sampler() {
     Source source;
-    if (match(Token::Type::kSampler, &source))
+    if (match(Token::Type::kSampler, &source)) {
         return builder_.ty.sampler(source, ast::SamplerKind::kSampler);
+    }
 
-    if (match(Token::Type::kComparisonSampler, &source))
+    if (match(Token::Type::kComparisonSampler, &source)) {
         return builder_.ty.sampler(source, ast::SamplerKind::kComparisonSampler);
+    }
 
     return Failure::kNoMatch;
 }
@@ -690,23 +726,29 @@
 //  | TEXTURE_SAMPLED_CUBE
 //  | TEXTURE_SAMPLED_CUBE_ARRAY
 Maybe<const ast::TextureDimension> ParserImpl::sampled_texture() {
-    if (match(Token::Type::kTextureSampled1d))
+    if (match(Token::Type::kTextureSampled1d)) {
         return ast::TextureDimension::k1d;
+    }
 
-    if (match(Token::Type::kTextureSampled2d))
+    if (match(Token::Type::kTextureSampled2d)) {
         return ast::TextureDimension::k2d;
+    }
 
-    if (match(Token::Type::kTextureSampled2dArray))
+    if (match(Token::Type::kTextureSampled2dArray)) {
         return ast::TextureDimension::k2dArray;
+    }
 
-    if (match(Token::Type::kTextureSampled3d))
+    if (match(Token::Type::kTextureSampled3d)) {
         return ast::TextureDimension::k3d;
+    }
 
-    if (match(Token::Type::kTextureSampledCube))
+    if (match(Token::Type::kTextureSampledCube)) {
         return ast::TextureDimension::kCube;
+    }
 
-    if (match(Token::Type::kTextureSampledCubeArray))
+    if (match(Token::Type::kTextureSampledCubeArray)) {
         return ast::TextureDimension::kCubeArray;
+    }
 
     return Failure::kNoMatch;
 }
@@ -725,8 +767,9 @@
 // multisampled_texture
 //  : TEXTURE_MULTISAMPLED_2D
 Maybe<const ast::TextureDimension> ParserImpl::multisampled_texture() {
-    if (match(Token::Type::kTextureMultisampled2d))
+    if (match(Token::Type::kTextureMultisampled2d)) {
         return ast::TextureDimension::k2d;
+    }
 
     return Failure::kNoMatch;
 }
@@ -737,14 +780,18 @@
 //  | TEXTURE_STORAGE_2D_ARRAY
 //  | TEXTURE_STORAGE_3D
 Maybe<const ast::TextureDimension> ParserImpl::storage_texture() {
-    if (match(Token::Type::kTextureStorage1d))
+    if (match(Token::Type::kTextureStorage1d)) {
         return ast::TextureDimension::k1d;
-    if (match(Token::Type::kTextureStorage2d))
+    }
+    if (match(Token::Type::kTextureStorage2d)) {
         return ast::TextureDimension::k2d;
-    if (match(Token::Type::kTextureStorage2dArray))
+    }
+    if (match(Token::Type::kTextureStorage2dArray)) {
         return ast::TextureDimension::k2dArray;
-    if (match(Token::Type::kTextureStorage3d))
+    }
+    if (match(Token::Type::kTextureStorage3d)) {
         return ast::TextureDimension::k3d;
+    }
 
     return Failure::kNoMatch;
 }
@@ -850,37 +897,45 @@
 Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_variable_ident_decl(std::string_view use,
                                                                            bool allow_inferred) {
     auto ident = expect_ident(use);
-    if (ident.errored)
+    if (ident.errored) {
         return Failure::kErrored;
+    }
 
     if (allow_inferred && !peek_is(Token::Type::kColon)) {
         return TypedIdentifier{nullptr, ident.value, ident.source};
     }
 
-    if (!expect(use, Token::Type::kColon))
+    if (!expect(use, Token::Type::kColon)) {
         return Failure::kErrored;
+    }
 
     auto t = peek();
     auto type = type_decl();
-    if (type.errored)
+    if (type.errored) {
         return Failure::kErrored;
-    if (!type.matched)
+    }
+    if (!type.matched) {
         return add_error(t.source(), "invalid type", use);
+    }
 
     return TypedIdentifier{type.value, ident.value, ident.source};
 }
 
 Expect<ast::Access> ParserImpl::expect_access(std::string_view use) {
     auto ident = expect_ident(use);
-    if (ident.errored)
+    if (ident.errored) {
         return Failure::kErrored;
+    }
 
-    if (ident.value == kReadAccess)
+    if (ident.value == kReadAccess) {
         return {ast::Access::kRead, ident.source};
-    if (ident.value == kWriteAccess)
+    }
+    if (ident.value == kWriteAccess) {
         return {ast::Access::kWrite, ident.source};
-    if (ident.value == kReadWriteAccess)
+    }
+    if (ident.value == kReadWriteAccess) {
         return {ast::Access::kReadWrite, ident.source};
+    }
 
     return add_error(ident.source, "invalid value for access control");
 }
@@ -920,24 +975,29 @@
 // type_alias
 //   : TYPE IDENT EQUAL type_decl
 Maybe<const ast::Alias*> ParserImpl::type_alias() {
-    if (!peek_is(Token::Type::kType))
+    if (!peek_is(Token::Type::kType)) {
         return Failure::kNoMatch;
+    }
 
     auto t = next();
     const char* use = "type alias";
 
     auto name = expect_ident(use);
-    if (name.errored)
+    if (name.errored) {
         return Failure::kErrored;
+    }
 
-    if (!expect(use, Token::Type::kEqual))
+    if (!expect(use, Token::Type::kEqual)) {
         return Failure::kErrored;
+    }
 
     auto type = type_decl();
-    if (type.errored)
+    if (type.errored) {
         return Failure::kErrored;
-    if (!type.matched)
+    }
+    if (!type.matched) {
         return add_error(peek(), "invalid type alias");
+    }
 
     return builder_.ty.alias(make_source_range_from(t.source()), name.value, type.value);
 }
@@ -973,17 +1033,25 @@
         return builder_.create<ast::TypeName>(source, builder_.Symbols().Register(t.to_str()));
     }
 
-    if (match(Token::Type::kBool, &source))
+    if (match(Token::Type::kBool, &source)) {
         return builder_.ty.bool_(source);
+    }
 
-    if (match(Token::Type::kF32, &source))
+    if (match(Token::Type::kF16, &source)) {
+        return builder_.ty.f16(source);
+    }
+
+    if (match(Token::Type::kF32, &source)) {
         return builder_.ty.f32(source);
+    }
 
-    if (match(Token::Type::kI32, &source))
+    if (match(Token::Type::kI32, &source)) {
         return builder_.ty.i32(source);
+    }
 
-    if (match(Token::Type::kU32, &source))
+    if (match(Token::Type::kU32, &source)) {
         return builder_.ty.u32(source);
+    }
 
     if (t.IsVector()) {
         next();  // Consume the peek
@@ -1008,20 +1076,24 @@
     }
 
     auto texture_or_sampler = texture_samplers();
-    if (texture_or_sampler.errored)
+    if (texture_or_sampler.errored) {
         return Failure::kErrored;
-    if (texture_or_sampler.matched)
+    }
+    if (texture_or_sampler.matched) {
         return texture_or_sampler;
+    }
 
     return Failure::kNoMatch;
 }
 
 Expect<const ast::Type*> ParserImpl::expect_type(std::string_view use) {
     auto type = type_decl();
-    if (type.errored)
+    if (type.errored) {
         return Failure::kErrored;
-    if (!type.matched)
+    }
+    if (!type.matched) {
         return add_error(peek().source(), "invalid type", use);
+    }
     return type.value;
 }
 
@@ -1105,8 +1177,9 @@
 
     auto subtype = expect_lt_gt_block(use, [&]() -> Expect<const ast::Type*> {
         auto type = expect_type(use);
-        if (type.errored)
+        if (type.errored) {
             return Failure::kErrored;
+        }
 
         if (match(Token::Type::kComma)) {
             auto expr = primary_expression();
@@ -1167,20 +1240,25 @@
 Expect<ast::StorageClass> ParserImpl::expect_storage_class(std::string_view use) {
     auto source = peek().source();
 
-    if (match(Token::Type::kUniform))
+    if (match(Token::Type::kUniform)) {
         return {ast::StorageClass::kUniform, source};
+    }
 
-    if (match(Token::Type::kWorkgroup))
+    if (match(Token::Type::kWorkgroup)) {
         return {ast::StorageClass::kWorkgroup, source};
+    }
 
-    if (match(Token::Type::kStorage))
+    if (match(Token::Type::kStorage)) {
         return {ast::StorageClass::kStorage, source};
+    }
 
-    if (match(Token::Type::kPrivate))
+    if (match(Token::Type::kPrivate)) {
         return {ast::StorageClass::kPrivate, source};
+    }
 
-    if (match(Token::Type::kFunction))
+    if (match(Token::Type::kFunction)) {
         return {ast::StorageClass::kFunction, source};
+    }
 
     return add_error(source, "invalid storage class", use);
 }
@@ -1191,16 +1269,19 @@
     auto t = peek();
     auto source = t.source();
 
-    if (!match(Token::Type::kStruct))
+    if (!match(Token::Type::kStruct)) {
         return Failure::kNoMatch;
+    }
 
     auto name = expect_ident("struct declaration");
-    if (name.errored)
+    if (name.errored) {
         return Failure::kErrored;
+    }
 
     auto body = expect_struct_body_decl();
-    if (body.errored)
+    if (body.errored) {
         return Failure::kErrored;
+    }
 
     auto sym = builder_.Symbols().Register(name.value);
     return create<ast::Struct>(source, sym, std::move(body.value), ast::AttributeList{});
@@ -1235,8 +1316,9 @@
                 next();
                 continue;
             }
-            if (!match(Token::Type::kComma))
+            if (!match(Token::Type::kComma)) {
                 break;
+            }
         }
         if (errored) {
             return Failure::kErrored;
@@ -1254,8 +1336,9 @@
     }
 
     auto decl = expect_variable_ident_decl("struct member");
-    if (decl.errored)
+    if (decl.errored) {
         return Failure::kErrored;
+    }
 
     return create<ast::StructMember>(decl->source, builder_.Symbols().Register(decl->name),
                                      decl->type, std::move(attrs.value));
@@ -1277,17 +1360,20 @@
         }
         return Failure::kErrored;
     }
-    if (!header.matched)
+    if (!header.matched) {
         return Failure::kNoMatch;
+    }
 
     bool errored = false;
 
     auto body = expect_body_stmt();
-    if (body.errored)
+    if (body.errored) {
         errored = true;
+    }
 
-    if (errored)
+    if (errored) {
         return Failure::kErrored;
+    }
 
     return create<ast::Function>(header->source, builder_.Symbols().Register(header->name),
                                  header->params, header->return_type, body.value, attrs,
@@ -1367,12 +1453,14 @@
         }
 
         auto param = expect_param();
-        if (param.errored)
+        if (param.errored) {
             return Failure::kErrored;
+        }
         ret.push_back(param.value);
 
-        if (!match(Token::Type::kComma))
+        if (!match(Token::Type::kComma)) {
             break;
+        }
     }
 
     return ret;
@@ -1384,8 +1472,9 @@
     auto attrs = attribute_list();
 
     auto decl = expect_variable_ident_decl("parameter");
-    if (decl.errored)
+    if (decl.errored) {
         return Failure::kErrored;
+    }
 
     auto* var = create<ast::Variable>(decl->source,                             // source
                                       builder_.Symbols().Register(decl->name),  // symbol
@@ -1427,12 +1516,14 @@
 
 Expect<ast::Builtin> ParserImpl::expect_builtin() {
     auto ident = expect_ident("builtin");
-    if (ident.errored)
+    if (ident.errored) {
         return Failure::kErrored;
+    }
 
     ast::Builtin builtin = ident_to_builtin(ident.value);
-    if (builtin == ast::Builtin::kNone)
+    if (builtin == ast::Builtin::kNone) {
         return add_error(ident.source, "invalid value for builtin attribute");
+    }
 
     return {builtin, ident.source};
 }
@@ -1442,8 +1533,9 @@
 Expect<ast::BlockStatement*> ParserImpl::expect_body_stmt() {
     return expect_brace_block("", [&]() -> Expect<ast::BlockStatement*> {
         auto stmts = expect_statements();
-        if (stmts.errored)
+        if (stmts.errored) {
             return Failure::kErrored;
+        }
         return create<ast::BlockStatement>(Source{}, stmts.value);
     });
 }
@@ -1453,10 +1545,12 @@
 Expect<const ast::Expression*> ParserImpl::expect_paren_rhs_stmt() {
     return expect_paren_block("", [&]() -> Expect<const ast::Expression*> {
         auto expr = logical_or_expression();
-        if (expr.errored)
+        if (expr.errored) {
             return Failure::kErrored;
-        if (!expr.matched)
+        }
+        if (!expr.matched) {
             return add_error(peek(), "unable to parse expression");
+        }
 
         return expr.value;
     });
@@ -1479,8 +1573,9 @@
         }
     }
 
-    if (errored)
+    if (errored) {
         return Failure::kErrored;
+    }
 
     return stmts;
 }
@@ -1510,39 +1605,50 @@
     // Non-block statments that error can resynchronize on semicolon.
     auto stmt = sync(Token::Type::kSemicolon, [&] { return non_block_statement(); });
 
-    if (stmt.errored)
+    if (stmt.errored) {
         return Failure::kErrored;
-    if (stmt.matched)
+    }
+    if (stmt.matched) {
         return stmt;
+    }
 
     auto stmt_if = if_stmt();
-    if (stmt_if.errored)
+    if (stmt_if.errored) {
         return Failure::kErrored;
-    if (stmt_if.matched)
+    }
+    if (stmt_if.matched) {
         return stmt_if.value;
+    }
 
     auto sw = switch_stmt();
-    if (sw.errored)
+    if (sw.errored) {
         return Failure::kErrored;
-    if (sw.matched)
+    }
+    if (sw.matched) {
         return sw.value;
+    }
 
     auto loop = loop_stmt();
-    if (loop.errored)
+    if (loop.errored) {
         return Failure::kErrored;
-    if (loop.matched)
+    }
+    if (loop.matched) {
         return loop.value;
+    }
 
     auto stmt_for = for_stmt();
-    if (stmt_for.errored)
+    if (stmt_for.errored) {
         return Failure::kErrored;
-    if (stmt_for.matched)
+    }
+    if (stmt_for.matched) {
         return stmt_for.value;
+    }
 
     if (peek_is(Token::Type::kBraceLeft)) {
         auto body = expect_body_stmt();
-        if (body.errored)
+        if (body.errored) {
             return Failure::kErrored;
+        }
         return body.value;
     }
 
@@ -1562,50 +1668,64 @@
 Maybe<const ast::Statement*> ParserImpl::non_block_statement() {
     auto stmt = [&]() -> Maybe<const ast::Statement*> {
         auto ret_stmt = return_stmt();
-        if (ret_stmt.errored)
+        if (ret_stmt.errored) {
             return Failure::kErrored;
-        if (ret_stmt.matched)
+        }
+        if (ret_stmt.matched) {
             return ret_stmt.value;
+        }
 
         auto func = func_call_stmt();
-        if (func.errored)
+        if (func.errored) {
             return Failure::kErrored;
-        if (func.matched)
+        }
+        if (func.matched) {
             return func.value;
+        }
 
         auto var = variable_stmt();
-        if (var.errored)
+        if (var.errored) {
             return Failure::kErrored;
-        if (var.matched)
+        }
+        if (var.matched) {
             return var.value;
+        }
 
         auto b = break_stmt();
-        if (b.errored)
+        if (b.errored) {
             return Failure::kErrored;
-        if (b.matched)
+        }
+        if (b.matched) {
             return b.value;
+        }
 
         auto cont = continue_stmt();
-        if (cont.errored)
+        if (cont.errored) {
             return Failure::kErrored;
-        if (cont.matched)
+        }
+        if (cont.matched) {
             return cont.value;
+        }
 
         auto assign = assignment_stmt();
-        if (assign.errored)
+        if (assign.errored) {
             return Failure::kErrored;
-        if (assign.matched)
+        }
+        if (assign.matched) {
             return assign.value;
+        }
 
         Source source;
-        if (match(Token::Type::kDiscard, &source))
+        if (match(Token::Type::kDiscard, &source)) {
             return create<ast::DiscardStatement>(source);
+        }
 
         return Failure::kNoMatch;
     }();
 
-    if (stmt.matched && !expect(stmt->Name(), Token::Type::kSemicolon))
+    if (stmt.matched && !expect(stmt->Name(), Token::Type::kSemicolon)) {
         return Failure::kErrored;
+    }
 
     return stmt;
 }
@@ -1614,15 +1734,18 @@
 //   : RETURN logical_or_expression?
 Maybe<const ast::ReturnStatement*> ParserImpl::return_stmt() {
     Source source;
-    if (!match(Token::Type::kReturn, &source))
+    if (!match(Token::Type::kReturn, &source)) {
         return Failure::kNoMatch;
+    }
 
-    if (peek_is(Token::Type::kSemicolon))
+    if (peek_is(Token::Type::kSemicolon)) {
         return create<ast::ReturnStatement>(source, nullptr);
+    }
 
     auto expr = logical_or_expression();
-    if (expr.errored)
+    if (expr.errored) {
         return Failure::kErrored;
+    }
 
     // TODO(bclayton): Check matched?
     return create<ast::ReturnStatement>(source, expr.value);
@@ -1636,17 +1759,21 @@
     if (match(Token::Type::kLet)) {
         auto decl = expect_variable_ident_decl("let declaration",
                                                /*allow_inferred = */ true);
-        if (decl.errored)
+        if (decl.errored) {
             return Failure::kErrored;
+        }
 
-        if (!expect("let declaration", Token::Type::kEqual))
+        if (!expect("let declaration", Token::Type::kEqual)) {
             return Failure::kErrored;
+        }
 
         auto constructor = logical_or_expression();
-        if (constructor.errored)
+        if (constructor.errored) {
             return Failure::kErrored;
-        if (!constructor.matched)
+        }
+        if (!constructor.matched) {
             return add_error(peek(), "missing constructor for let declaration");
+        }
 
         auto* var = create<ast::Variable>(decl->source,                             // source
                                           builder_.Symbols().Register(decl->name),  // symbol
@@ -1662,18 +1789,22 @@
     }
 
     auto decl = variable_decl(/*allow_inferred = */ true);
-    if (decl.errored)
+    if (decl.errored) {
         return Failure::kErrored;
-    if (!decl.matched)
+    }
+    if (!decl.matched) {
         return Failure::kNoMatch;
+    }
 
     const ast::Expression* constructor = nullptr;
     if (match(Token::Type::kEqual)) {
         auto constructor_expr = logical_or_expression();
-        if (constructor_expr.errored)
+        if (constructor_expr.errored) {
             return Failure::kErrored;
-        if (!constructor_expr.matched)
+        }
+        if (!constructor_expr.matched) {
             return add_error(peek(), "missing constructor for variable declaration");
+        }
 
         constructor = constructor_expr.value;
     }
@@ -1777,12 +1908,14 @@
 //   : SWITCH paren_rhs_stmt BRACKET_LEFT switch_body+ BRACKET_RIGHT
 Maybe<const ast::SwitchStatement*> ParserImpl::switch_stmt() {
     Source source;
-    if (!match(Token::Type::kSwitch, &source))
+    if (!match(Token::Type::kSwitch, &source)) {
         return Failure::kNoMatch;
+    }
 
     auto condition = logical_or_expression();
-    if (condition.errored)
+    if (condition.errored) {
         return Failure::kErrored;
+    }
     if (!condition.matched) {
         return add_error(peek(), "unable to parse selector expression");
     }
@@ -1796,17 +1929,20 @@
                 errored = true;
                 continue;
             }
-            if (!stmt.matched)
+            if (!stmt.matched) {
                 break;
+            }
             list.push_back(stmt.value);
         }
-        if (errored)
+        if (errored) {
             return Failure::kErrored;
+        }
         return list;
     });
 
-    if (body.errored)
+    if (body.errored) {
         return Failure::kErrored;
+    }
 
     return create<ast::SwitchStatement>(source, condition.value, body.value);
 }
@@ -1815,8 +1951,9 @@
 //   : CASE case_selectors COLON? BRACKET_LEFT case_body BRACKET_RIGHT
 //   | DEFAULT COLON? BRACKET_LEFT case_body BRACKET_RIGHT
 Maybe<const ast::CaseStatement*> ParserImpl::switch_body() {
-    if (!peek_is(Token::Type::kCase) && !peek_is(Token::Type::kDefault))
+    if (!peek_is(Token::Type::kCase) && !peek_is(Token::Type::kDefault)) {
         return Failure::kNoMatch;
+    }
 
     auto t = next();
     auto source = t.source();
@@ -1824,8 +1961,9 @@
     ast::CaseSelectorList selector_list;
     if (t.Is(Token::Type::kCase)) {
         auto selectors = expect_case_selectors();
-        if (selectors.errored)
+        if (selectors.errored) {
             return Failure::kErrored;
+        }
 
         selector_list = std::move(selectors.value);
     }
@@ -1836,10 +1974,12 @@
     const char* use = "case statement";
     auto body = expect_brace_block(use, [&] { return case_body(); });
 
-    if (body.errored)
+    if (body.errored) {
         return Failure::kErrored;
-    if (!body.matched)
+    }
+    if (!body.matched) {
         return add_error(body.source, "expected case body");
+    }
 
     return create<ast::CaseStatement>(source, selector_list, body.value);
 }
@@ -1866,8 +2006,9 @@
         }
     }
 
-    if (selectors.empty())
+    if (selectors.empty()) {
         return add_error(peek(), "unable to parse case selectors");
+    }
 
     return selectors;
 }
@@ -1881,18 +2022,21 @@
     while (continue_parsing()) {
         Source source;
         if (match(Token::Type::kFallthrough, &source)) {
-            if (!expect("fallthrough statement", Token::Type::kSemicolon))
+            if (!expect("fallthrough statement", Token::Type::kSemicolon)) {
                 return Failure::kErrored;
+            }
 
             stmts.emplace_back(create<ast::FallthroughStatement>(source));
             break;
         }
 
         auto stmt = statement();
-        if (stmt.errored)
+        if (stmt.errored) {
             return Failure::kErrored;
-        if (!stmt.matched)
+        }
+        if (!stmt.matched) {
             break;
+        }
 
         stmts.emplace_back(stmt.value);
     }
@@ -1904,17 +2048,20 @@
 //   : LOOP BRACKET_LEFT statements continuing_stmt? BRACKET_RIGHT
 Maybe<const ast::LoopStatement*> ParserImpl::loop_stmt() {
     Source source;
-    if (!match(Token::Type::kLoop, &source))
+    if (!match(Token::Type::kLoop, &source)) {
         return Failure::kNoMatch;
+    }
 
     return expect_brace_block("loop", [&]() -> Maybe<const ast::LoopStatement*> {
         auto stmts = expect_statements();
-        if (stmts.errored)
+        if (stmts.errored) {
             return Failure::kErrored;
+        }
 
         auto continuing = continuing_stmt();
-        if (continuing.errored)
+        if (continuing.errored) {
             return Failure::kErrored;
+        }
 
         auto* body = create<ast::BlockStatement>(source, stmts.value);
         return create<ast::LoopStatement>(source, body, continuing.value);
@@ -1932,22 +2079,28 @@
 // func_call_stmt)?
 Maybe<const ast::Statement*> ParserImpl::for_header_initializer() {
     auto call = func_call_stmt();
-    if (call.errored)
+    if (call.errored) {
         return Failure::kErrored;
-    if (call.matched)
+    }
+    if (call.matched) {
         return call.value;
+    }
 
     auto var = variable_stmt();
-    if (var.errored)
+    if (var.errored) {
         return Failure::kErrored;
-    if (var.matched)
+    }
+    if (var.matched) {
         return var.value;
+    }
 
     auto assign = assignment_stmt();
-    if (assign.errored)
+    if (assign.errored) {
         return Failure::kErrored;
-    if (assign.matched)
+    }
+    if (assign.matched) {
         return assign.value;
+    }
 
     return Failure::kNoMatch;
 }
@@ -1955,16 +2108,20 @@
 // (increment_stmt | decrement_stmt | assignment_stmt | func_call_stmt)?
 Maybe<const ast::Statement*> ParserImpl::for_header_continuing() {
     auto call_stmt = func_call_stmt();
-    if (call_stmt.errored)
+    if (call_stmt.errored) {
         return Failure::kErrored;
-    if (call_stmt.matched)
+    }
+    if (call_stmt.matched) {
         return call_stmt.value;
+    }
 
     auto assign = assignment_stmt();
-    if (assign.errored)
+    if (assign.errored) {
         return Failure::kErrored;
-    if (assign.matched)
+    }
+    if (assign.matched) {
         return assign.value;
+    }
 
     return Failure::kNoMatch;
 }
@@ -1976,22 +2133,27 @@
 //      (assignment_stmt | func_call_stmt)?
 Expect<std::unique_ptr<ForHeader>> ParserImpl::expect_for_header() {
     auto initializer = for_header_initializer();
-    if (initializer.errored)
+    if (initializer.errored) {
         return Failure::kErrored;
+    }
 
-    if (!expect("initializer in for loop", Token::Type::kSemicolon))
+    if (!expect("initializer in for loop", Token::Type::kSemicolon)) {
         return Failure::kErrored;
+    }
 
     auto condition = logical_or_expression();
-    if (condition.errored)
+    if (condition.errored) {
         return Failure::kErrored;
+    }
 
-    if (!expect("condition in for loop", Token::Type::kSemicolon))
+    if (!expect("condition in for loop", Token::Type::kSemicolon)) {
         return Failure::kErrored;
+    }
 
     auto continuing = for_header_continuing();
-    if (continuing.errored)
+    if (continuing.errored) {
         return Failure::kErrored;
+    }
 
     return std::make_unique<ForHeader>(initializer.value, condition.value, continuing.value);
 }
@@ -2000,16 +2162,19 @@
 //   : FOR PAREN_LEFT for_header PAREN_RIGHT BRACE_LEFT statements BRACE_RIGHT
 Maybe<const ast::ForLoopStatement*> ParserImpl::for_stmt() {
     Source source;
-    if (!match(Token::Type::kFor, &source))
+    if (!match(Token::Type::kFor, &source)) {
         return Failure::kNoMatch;
+    }
 
     auto header = expect_paren_block("for loop", [&] { return expect_for_header(); });
-    if (header.errored)
+    if (header.errored) {
         return Failure::kErrored;
+    }
 
     auto stmts = expect_brace_block("for loop", [&] { return expect_statements(); });
-    if (stmts.errored)
+    if (stmts.errored) {
         return Failure::kErrored;
+    }
 
     return create<ast::ForLoopStatement>(source, header->initializer, header->condition,
                                          header->continuing,
@@ -2021,8 +2186,9 @@
 Maybe<const ast::CallStatement*> ParserImpl::func_call_stmt() {
     auto t = peek();
     auto t2 = peek(1);
-    if (!t.IsIdentifier() || !t2.Is(Token::Type::kParenLeft))
+    if (!t.IsIdentifier() || !t2.Is(Token::Type::kParenLeft)) {
         return Failure::kNoMatch;
+    }
 
     next();  // Consume the first peek
 
@@ -2030,8 +2196,9 @@
     auto name = t.to_str();
 
     auto params = expect_argument_expression_list("function call");
-    if (params.errored)
+    if (params.errored) {
         return Failure::kErrored;
+    }
 
     return create<ast::CallStatement>(
         source,
@@ -2044,8 +2211,9 @@
 //   : BREAK
 Maybe<const ast::BreakStatement*> ParserImpl::break_stmt() {
     Source source;
-    if (!match(Token::Type::kBreak, &source))
+    if (!match(Token::Type::kBreak, &source)) {
         return Failure::kNoMatch;
+    }
 
     return create<ast::BreakStatement>(source);
 }
@@ -2054,8 +2222,9 @@
 //   : CONTINUE
 Maybe<const ast::ContinueStatement*> ParserImpl::continue_stmt() {
     Source source;
-    if (!match(Token::Type::kContinue, &source))
+    if (!match(Token::Type::kContinue, &source)) {
         return Failure::kNoMatch;
+    }
 
     return create<ast::ContinueStatement>(source);
 }
@@ -2063,8 +2232,9 @@
 // continuing_stmt
 //   : CONTINUING body_stmt
 Maybe<const ast::BlockStatement*> ParserImpl::continuing_stmt() {
-    if (!match(Token::Type::kContinuing))
+    if (!match(Token::Type::kContinuing)) {
         return create<ast::BlockStatement>(Source{}, ast::StatementList{});
+    }
 
     return expect_body_stmt();
 }
@@ -2100,12 +2270,14 @@
         const char* use = "bitcast expression";
 
         auto type = expect_lt_gt_block(use, [&] { return expect_type(use); });
-        if (type.errored)
+        if (type.errored) {
             return Failure::kErrored;
+        }
 
         auto params = expect_paren_rhs_stmt();
-        if (params.errored)
+        if (params.errored) {
             return Failure::kErrored;
+        }
 
         return create<ast::BitcastExpression>(source, type.value, params.value);
     }
@@ -2118,8 +2290,9 @@
 
         if (peek_is(Token::Type::kParenLeft)) {
             auto params = expect_argument_expression_list("function call");
-            if (params.errored)
+            if (params.errored) {
                 return Failure::kErrored;
+            }
 
             return create<ast::CallExpression>(source, ident, std::move(params.value));
         }
@@ -2128,12 +2301,14 @@
     }
 
     auto type = type_decl();
-    if (type.errored)
+    if (type.errored) {
         return Failure::kErrored;
+    }
     if (type.matched) {
         auto params = expect_argument_expression_list("type constructor");
-        if (params.errored)
+        if (params.errored) {
             return Failure::kErrored;
+        }
 
         return builder_.Construct(source, type.value, std::move(params.value));
     }
@@ -2152,8 +2327,9 @@
         if (match(Token::Type::kBracketLeft, &source)) {
             auto res = sync(Token::Type::kBracketRight, [&]() -> Maybe<const ast::Expression*> {
                 auto param = logical_or_expression();
-                if (param.errored)
+                if (param.errored) {
                     return Failure::kErrored;
+                }
                 if (!param.matched) {
                     return add_error(peek(), "unable to parse expression inside []");
                 }
@@ -2195,10 +2371,12 @@
 //   : primary_expression postfix_expr
 Maybe<const ast::Expression*> ParserImpl::singular_expression() {
     auto prefix = primary_expression();
-    if (prefix.errored)
+    if (prefix.errored) {
         return Failure::kErrored;
-    if (!prefix.matched)
+    }
+    if (!prefix.matched) {
         return Failure::kNoMatch;
+    }
 
     return postfix_expression(prefix.value);
 }
@@ -2289,22 +2467,24 @@
 Expect<const ast::Expression*> ParserImpl::expect_multiplicative_expr(const ast::Expression* lhs) {
     while (continue_parsing()) {
         ast::BinaryOp op = ast::BinaryOp::kNone;
-        if (peek_is(Token::Type::kStar))
+        if (peek_is(Token::Type::kStar)) {
             op = ast::BinaryOp::kMultiply;
-        else if (peek_is(Token::Type::kForwardSlash))
+        } else if (peek_is(Token::Type::kForwardSlash)) {
             op = ast::BinaryOp::kDivide;
-        else if (peek_is(Token::Type::kMod))
+        } else if (peek_is(Token::Type::kMod)) {
             op = ast::BinaryOp::kModulo;
-        else
+        } else {
             return lhs;
+        }
 
         auto t = next();
         auto source = t.source();
         auto name = t.to_name();
 
         auto rhs = unary_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
+        }
         if (!rhs.matched) {
             return add_error(peek(),
                              "unable to parse right side of " + std::string(name) + " expression");
@@ -2319,10 +2499,12 @@
 //   : unary_expression multiplicative_expr
 Maybe<const ast::Expression*> ParserImpl::multiplicative_expression() {
     auto lhs = unary_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_multiplicative_expr(lhs.value);
 }
@@ -2334,21 +2516,24 @@
 Expect<const ast::Expression*> ParserImpl::expect_additive_expr(const ast::Expression* lhs) {
     while (continue_parsing()) {
         ast::BinaryOp op = ast::BinaryOp::kNone;
-        if (peek_is(Token::Type::kPlus))
+        if (peek_is(Token::Type::kPlus)) {
             op = ast::BinaryOp::kAdd;
-        else if (peek_is(Token::Type::kMinus))
+        } else if (peek_is(Token::Type::kMinus)) {
             op = ast::BinaryOp::kSubtract;
-        else
+        } else {
             return lhs;
+        }
 
         auto t = next();
         auto source = t.source();
 
         auto rhs = multiplicative_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
-        if (!rhs.matched)
+        }
+        if (!rhs.matched) {
             return add_error(peek(), "unable to parse right side of + expression");
+        }
 
         lhs = create<ast::BinaryExpression>(source, op, lhs, rhs.value);
     }
@@ -2359,10 +2544,12 @@
 //   : multiplicative_expression additive_expr
 Maybe<const ast::Expression*> ParserImpl::additive_expression() {
     auto lhs = multiplicative_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_additive_expr(lhs.value);
 }
@@ -2388,8 +2575,9 @@
         auto t = next();
         auto source = t.source();
         auto rhs = additive_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
+        }
         if (!rhs.matched) {
             return add_error(peek(),
                              std::string("unable to parse right side of ") + name + " expression");
@@ -2404,10 +2592,12 @@
 //   : additive_expression shift_expr
 Maybe<const ast::Expression*> ParserImpl::shift_expression() {
     auto lhs = additive_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_shift_expr(lhs.value);
 }
@@ -2421,24 +2611,26 @@
 Expect<const ast::Expression*> ParserImpl::expect_relational_expr(const ast::Expression* lhs) {
     while (continue_parsing()) {
         ast::BinaryOp op = ast::BinaryOp::kNone;
-        if (peek_is(Token::Type::kLessThan))
+        if (peek_is(Token::Type::kLessThan)) {
             op = ast::BinaryOp::kLessThan;
-        else if (peek_is(Token::Type::kGreaterThan))
+        } else if (peek_is(Token::Type::kGreaterThan)) {
             op = ast::BinaryOp::kGreaterThan;
-        else if (peek_is(Token::Type::kLessThanEqual))
+        } else if (peek_is(Token::Type::kLessThanEqual)) {
             op = ast::BinaryOp::kLessThanEqual;
-        else if (peek_is(Token::Type::kGreaterThanEqual))
+        } else if (peek_is(Token::Type::kGreaterThanEqual)) {
             op = ast::BinaryOp::kGreaterThanEqual;
-        else
+        } else {
             return lhs;
+        }
 
         auto t = next();
         auto source = t.source();
         auto name = t.to_name();
 
         auto rhs = shift_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
+        }
         if (!rhs.matched) {
             return add_error(peek(),
                              "unable to parse right side of " + std::string(name) + " expression");
@@ -2453,10 +2645,12 @@
 //   : shift_expression relational_expr
 Maybe<const ast::Expression*> ParserImpl::relational_expression() {
     auto lhs = shift_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_relational_expr(lhs.value);
 }
@@ -2468,20 +2662,22 @@
 Expect<const ast::Expression*> ParserImpl::expect_equality_expr(const ast::Expression* lhs) {
     while (continue_parsing()) {
         ast::BinaryOp op = ast::BinaryOp::kNone;
-        if (peek_is(Token::Type::kEqualEqual))
+        if (peek_is(Token::Type::kEqualEqual)) {
             op = ast::BinaryOp::kEqual;
-        else if (peek_is(Token::Type::kNotEqual))
+        } else if (peek_is(Token::Type::kNotEqual)) {
             op = ast::BinaryOp::kNotEqual;
-        else
+        } else {
             return lhs;
+        }
 
         auto t = next();
         auto source = t.source();
         auto name = t.to_name();
 
         auto rhs = relational_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
+        }
         if (!rhs.matched) {
             return add_error(peek(),
                              "unable to parse right side of " + std::string(name) + " expression");
@@ -2496,10 +2692,12 @@
 //   : relational_expression equality_expr
 Maybe<const ast::Expression*> ParserImpl::equality_expression() {
     auto lhs = relational_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_equality_expr(lhs.value);
 }
@@ -2517,10 +2715,12 @@
         auto source = t.source();
 
         auto rhs = equality_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
-        if (!rhs.matched)
+        }
+        if (!rhs.matched) {
             return add_error(peek(), "unable to parse right side of & expression");
+        }
 
         lhs = create<ast::BinaryExpression>(source, ast::BinaryOp::kAnd, lhs, rhs.value);
     }
@@ -2531,10 +2731,12 @@
 //   : equality_expression and_expr
 Maybe<const ast::Expression*> ParserImpl::and_expression() {
     auto lhs = equality_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_and_expr(lhs.value);
 }
@@ -2545,14 +2747,17 @@
 Expect<const ast::Expression*> ParserImpl::expect_exclusive_or_expr(const ast::Expression* lhs) {
     while (continue_parsing()) {
         Source source;
-        if (!match(Token::Type::kXor, &source))
+        if (!match(Token::Type::kXor, &source)) {
             return lhs;
+        }
 
         auto rhs = and_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
-        if (!rhs.matched)
+        }
+        if (!rhs.matched) {
             return add_error(peek(), "unable to parse right side of ^ expression");
+        }
 
         lhs = create<ast::BinaryExpression>(source, ast::BinaryOp::kXor, lhs, rhs.value);
     }
@@ -2563,10 +2768,12 @@
 //   : and_expression exclusive_or_expr
 Maybe<const ast::Expression*> ParserImpl::exclusive_or_expression() {
     auto lhs = and_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_exclusive_or_expr(lhs.value);
 }
@@ -2577,14 +2784,17 @@
 Expect<const ast::Expression*> ParserImpl::expect_inclusive_or_expr(const ast::Expression* lhs) {
     while (continue_parsing()) {
         Source source;
-        if (!match(Token::Type::kOr))
+        if (!match(Token::Type::kOr, &source)) {
             return lhs;
+        }
 
         auto rhs = exclusive_or_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
-        if (!rhs.matched)
+        }
+        if (!rhs.matched) {
             return add_error(peek(), "unable to parse right side of | expression");
+        }
 
         lhs = create<ast::BinaryExpression>(source, ast::BinaryOp::kOr, lhs, rhs.value);
     }
@@ -2595,10 +2805,12 @@
 //   : exclusive_or_expression inclusive_or_expr
 Maybe<const ast::Expression*> ParserImpl::inclusive_or_expression() {
     auto lhs = exclusive_or_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_inclusive_or_expr(lhs.value);
 }
@@ -2616,10 +2828,12 @@
         auto source = t.source();
 
         auto rhs = inclusive_or_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
-        if (!rhs.matched)
+        }
+        if (!rhs.matched) {
             return add_error(peek(), "unable to parse right side of && expression");
+        }
 
         lhs = create<ast::BinaryExpression>(source, ast::BinaryOp::kLogicalAnd, lhs, rhs.value);
     }
@@ -2630,10 +2844,12 @@
 //   : inclusive_or_expression logical_and_expr
 Maybe<const ast::Expression*> ParserImpl::logical_and_expression() {
     auto lhs = inclusive_or_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_logical_and_expr(lhs.value);
 }
@@ -2644,14 +2860,17 @@
 Expect<const ast::Expression*> ParserImpl::expect_logical_or_expr(const ast::Expression* lhs) {
     while (continue_parsing()) {
         Source source;
-        if (!match(Token::Type::kOrOr))
+        if (!match(Token::Type::kOrOr, &source)) {
             return lhs;
+        }
 
         auto rhs = logical_and_expression();
-        if (rhs.errored)
+        if (rhs.errored) {
             return Failure::kErrored;
-        if (!rhs.matched)
+        }
+        if (!rhs.matched) {
             return add_error(peek(), "unable to parse right side of || expression");
+        }
 
         lhs = create<ast::BinaryExpression>(source, ast::BinaryOp::kLogicalOr, lhs, rhs.value);
     }
@@ -2662,10 +2881,12 @@
 //   : logical_and_expression logical_or_expr
 Maybe<const ast::Expression*> ParserImpl::logical_or_expression() {
     auto lhs = logical_and_expression();
-    if (lhs.errored)
+    if (lhs.errored) {
         return Failure::kErrored;
-    if (!lhs.matched)
+    }
+    if (!lhs.matched) {
         return Failure::kNoMatch;
+    }
 
     return expect_logical_or_expr(lhs.value);
 }
@@ -2852,8 +3073,9 @@
             return list;
         });
 
-        if (params.errored)
+        if (params.errored) {
             return Failure::kErrored;
+        }
 
         return builder_.Construct(source, type.value, params.value);
     }
@@ -2876,11 +3098,13 @@
         }
     }
 
-    if (errored)
+    if (errored) {
         return Failure::kErrored;
+    }
 
-    if (attrs.empty())
+    if (attrs.empty()) {
         return Failure::kNoMatch;
+    }
 
     return attrs;
 }
@@ -2888,10 +3112,12 @@
 Expect<const ast::Attribute*> ParserImpl::expect_attribute() {
     auto t = peek();
     auto attr = attribute();
-    if (attr.errored)
+    if (attr.errored) {
         return Failure::kErrored;
-    if (attr.matched)
+    }
+    if (attr.matched) {
         return attr.value;
+    }
     return add_error(t, "expected attribute");
 }
 
@@ -2907,8 +3133,9 @@
         const char* use = "location attribute";
         return expect_paren_block(use, [&]() -> Result {
             auto val = expect_positive_sint(use);
-            if (val.errored)
+            if (val.errored) {
                 return Failure::kErrored;
+            }
 
             return create<ast::LocationAttribute>(t.source(), val.value);
         });
@@ -2918,8 +3145,9 @@
         const char* use = "binding attribute";
         return expect_paren_block(use, [&]() -> Result {
             auto val = expect_positive_sint(use);
-            if (val.errored)
+            if (val.errored) {
                 return Failure::kErrored;
+            }
 
             return create<ast::BindingAttribute>(t.source(), val.value);
         });
@@ -2929,8 +3157,9 @@
         const char* use = "group attribute";
         return expect_paren_block(use, [&]() -> Result {
             auto val = expect_positive_sint(use);
-            if (val.errored)
+            if (val.errored) {
                 return Failure::kErrored;
+            }
 
             return create<ast::GroupAttribute>(t.source(), val.value);
         });
@@ -2976,8 +3205,9 @@
     if (t == kBuiltinAttribute) {
         return expect_paren_block("builtin attribute", [&]() -> Result {
             auto builtin = expect_builtin();
-            if (builtin.errored)
+            if (builtin.errored) {
                 return Failure::kErrored;
+            }
 
             return create<ast::BuiltinAttribute>(t.source(), builtin.value);
         });
@@ -3024,8 +3254,9 @@
     if (t == kStageAttribute) {
         return expect_paren_block("stage attribute", [&]() -> Result {
             auto stage = expect_pipeline_stage();
-            if (stage.errored)
+            if (stage.errored) {
                 return Failure::kErrored;
+            }
 
             return create<ast::StageAttribute>(t.source(), stage.value);
         });
@@ -3035,8 +3266,9 @@
         const char* use = "size attribute";
         return expect_paren_block(use, [&]() -> Result {
             auto val = expect_positive_sint(use);
-            if (val.errored)
+            if (val.errored) {
                 return Failure::kErrored;
+            }
 
             return create<ast::StructMemberSizeAttribute>(t.source(), val.value);
         });
@@ -3046,8 +3278,9 @@
         const char* use = "align attribute";
         return expect_paren_block(use, [&]() -> Result {
             auto val = expect_positive_sint(use);
-            if (val.errored)
+            if (val.errored) {
                 return Failure::kErrored;
+            }
 
             return create<ast::StructMemberAlignAttribute>(t.source(), val.value);
         });
@@ -3057,8 +3290,9 @@
         const char* use = "id attribute";
         return expect_paren_block(use, [&]() -> Result {
             auto val = expect_positive_sint(use);
-            if (val.errored)
+            if (val.errored) {
                 return Failure::kErrored;
+            }
 
             return create<ast::IdAttribute>(t.source(), val.value);
         });
@@ -3078,8 +3312,9 @@
 bool ParserImpl::match(Token::Type tok, Source* source /*= nullptr*/) {
     auto t = peek();
 
-    if (source != nullptr)
+    if (source != nullptr) {
         *source = t.source();
+    }
 
     if (t.Is(tok)) {
         next();
@@ -3148,22 +3383,26 @@
 
 Expect<uint32_t> ParserImpl::expect_positive_sint(std::string_view use) {
     auto sint = expect_sint(use);
-    if (sint.errored)
+    if (sint.errored) {
         return Failure::kErrored;
+    }
 
-    if (sint.value < 0)
+    if (sint.value < 0) {
         return add_error(sint.source, std::string(use) + " must be positive");
+    }
 
     return {static_cast<uint32_t>(sint.value), sint.source};
 }
 
 Expect<uint32_t> ParserImpl::expect_nonzero_positive_sint(std::string_view use) {
     auto sint = expect_sint(use);
-    if (sint.errored)
+    if (sint.errored) {
         return Failure::kErrored;
+    }
 
-    if (sint.value <= 0)
+    if (sint.value <= 0) {
         return add_error(sint.source, std::string(use) + " must be greater than 0");
+    }
 
     return {static_cast<uint32_t>(sint.value), sint.source};
 }
@@ -3196,11 +3435,13 @@
     return sync(end, [&]() -> T {
         auto res = body();
 
-        if (res.errored)
+        if (res.errored) {
             return Failure::kErrored;
+        }
 
-        if (!expect(use, end))
+        if (!expect(use, end)) {
             return Failure::kErrored;
+        }
 
         return res;
     });
diff --git a/src/tint/reader/wgsl/parser_impl_additive_expression_test.cc b/src/tint/reader/wgsl/parser_impl_additive_expression_test.cc
index 51cf90e..4573b84 100644
--- a/src/tint/reader/wgsl/parser_impl_additive_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_additive_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 4u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kAdd, rel->op);
diff --git a/src/tint/reader/wgsl/parser_impl_and_expression_test.cc b/src/tint/reader/wgsl/parser_impl_and_expression_test.cc
index 1ed8a9c..fd90460 100644
--- a/src/tint/reader/wgsl/parser_impl_and_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_and_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 4u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kAnd, rel->op);
diff --git a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
index 0fd6b80..bdf7eeb 100644
--- a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
@@ -23,41 +23,36 @@
 
 // Test a valid enable directive.
 TEST_F(EnableDirectiveTest, Valid) {
-    auto p = parser("enable InternalExtensionForTesting;");
+    auto p = parser("enable f16;");
     p->enable_directive();
     EXPECT_FALSE(p->has_error()) << p->error();
     auto program = p->program();
     auto& ast = program.AST();
-    EXPECT_EQ(ast.Extensions(),
-              ast::ExtensionSet{ast::Enable::ExtensionKind::kInternalExtensionForTesting});
-    EXPECT_EQ(ast.GlobalDeclarations().size(), 1u);
-    auto* node = ast.GlobalDeclarations()[0]->As<ast::Enable>();
-    EXPECT_TRUE(node != nullptr);
-    EXPECT_EQ(node->name, "InternalExtensionForTesting");
-    EXPECT_EQ(node->kind, ast::Enable::ExtensionKind::kInternalExtensionForTesting);
+    ASSERT_EQ(ast.Enables().size(), 1u);
+    auto* enable = ast.Enables()[0];
+    EXPECT_EQ(enable->extension, ast::Extension::kF16);
+    ASSERT_EQ(ast.GlobalDeclarations().size(), 1u);
+    EXPECT_EQ(ast.GlobalDeclarations()[0], enable);
 }
 
 // Test multiple enable directives for a same extension.
 TEST_F(EnableDirectiveTest, EnableMultipleTime) {
     auto p = parser(R"(
-enable InternalExtensionForTesting;
-enable InternalExtensionForTesting;
+enable f16;
+enable f16;
 )");
     p->translation_unit();
     EXPECT_FALSE(p->has_error()) << p->error();
     auto program = p->program();
     auto& ast = program.AST();
-    EXPECT_EQ(ast.Extensions(),
-              ast::ExtensionSet{ast::Enable::ExtensionKind::kInternalExtensionForTesting});
-    EXPECT_EQ(ast.GlobalDeclarations().size(), 2u);
-    auto* node1 = ast.GlobalDeclarations()[0]->As<ast::Enable>();
-    EXPECT_TRUE(node1 != nullptr);
-    EXPECT_EQ(node1->name, "InternalExtensionForTesting");
-    EXPECT_EQ(node1->kind, ast::Enable::ExtensionKind::kInternalExtensionForTesting);
-    auto* node2 = ast.GlobalDeclarations()[1]->As<ast::Enable>();
-    EXPECT_TRUE(node2 != nullptr);
-    EXPECT_EQ(node2->name, "InternalExtensionForTesting");
-    EXPECT_EQ(node2->kind, ast::Enable::ExtensionKind::kInternalExtensionForTesting);
+    ASSERT_EQ(ast.Enables().size(), 2u);
+    auto* enable_a = ast.Enables()[0];
+    auto* enable_b = ast.Enables()[1];
+    EXPECT_EQ(enable_a->extension, ast::Extension::kF16);
+    EXPECT_EQ(enable_b->extension, ast::Extension::kF16);
+    ASSERT_EQ(ast.GlobalDeclarations().size(), 2u);
+    EXPECT_EQ(ast.GlobalDeclarations()[0], enable_a);
+    EXPECT_EQ(ast.GlobalDeclarations()[1], enable_b);
 }
 
 // Test an unknown extension identifier.
@@ -69,42 +64,42 @@
     EXPECT_EQ(p->error(), "1:8: unsupported extension: 'NotAValidExtensionName'");
     auto program = p->program();
     auto& ast = program.AST();
-    EXPECT_EQ(ast.Extensions().size(), 0u);
+    EXPECT_EQ(ast.Enables().size(), 0u);
     EXPECT_EQ(ast.GlobalDeclarations().size(), 0u);
 }
 
-// Test an enable directive missing ending semiclon.
-TEST_F(EnableDirectiveTest, MissingEndingSemiclon) {
-    auto p = parser("enable InternalExtensionForTesting");
+// Test an enable directive missing ending semicolon.
+TEST_F(EnableDirectiveTest, MissingEndingSemicolon) {
+    auto p = parser("enable f16");
     p->translation_unit();
     EXPECT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:35: expected ';' for enable directive");
+    EXPECT_EQ(p->error(), "1:11: expected ';' for enable directive");
     auto program = p->program();
     auto& ast = program.AST();
-    EXPECT_EQ(ast.Extensions().size(), 0u);
+    EXPECT_EQ(ast.Enables().size(), 0u);
     EXPECT_EQ(ast.GlobalDeclarations().size(), 0u);
 }
 
 // Test using invalid tokens in an enable directive.
 TEST_F(EnableDirectiveTest, InvalidTokens) {
     {
-        auto p = parser("enable InternalExtensionForTesting<;");
+        auto p = parser("enable f16<;");
         p->translation_unit();
         EXPECT_TRUE(p->has_error());
-        EXPECT_EQ(p->error(), "1:35: expected ';' for enable directive");
+        EXPECT_EQ(p->error(), "1:11: expected ';' for enable directive");
         auto program = p->program();
         auto& ast = program.AST();
-        EXPECT_EQ(ast.Extensions().size(), 0u);
+        EXPECT_EQ(ast.Enables().size(), 0u);
         EXPECT_EQ(ast.GlobalDeclarations().size(), 0u);
     }
     {
-        auto p = parser("enable <InternalExtensionForTesting;");
+        auto p = parser("enable <f16;");
         p->translation_unit();
         EXPECT_TRUE(p->has_error());
         EXPECT_EQ(p->error(), "1:8: invalid extension name");
         auto program = p->program();
         auto& ast = program.AST();
-        EXPECT_EQ(ast.Extensions().size(), 0u);
+        EXPECT_EQ(ast.Enables().size(), 0u);
         EXPECT_EQ(ast.GlobalDeclarations().size(), 0u);
     }
     {
@@ -114,7 +109,7 @@
         EXPECT_EQ(p->error(), "1:8: invalid extension name");
         auto program = p->program();
         auto& ast = program.AST();
-        EXPECT_EQ(ast.Extensions().size(), 0u);
+        EXPECT_EQ(ast.Enables().size(), 0u);
         EXPECT_EQ(ast.GlobalDeclarations().size(), 0u);
     }
     {
@@ -124,7 +119,7 @@
         EXPECT_EQ(p->error(), "1:8: invalid extension name");
         auto program = p->program();
         auto& ast = program.AST();
-        EXPECT_EQ(ast.Extensions().size(), 0u);
+        EXPECT_EQ(ast.Enables().size(), 0u);
         EXPECT_EQ(ast.GlobalDeclarations().size(), 0u);
     }
 }
@@ -133,35 +128,39 @@
 TEST_F(EnableDirectiveTest, FollowingOtherGlobalDecl) {
     auto p = parser(R"(
 var<private> t: f32 = 0f;
-enable InternalExtensionForTesting;
+enable f16;
 )");
     p->translation_unit();
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), "3:1: enable directives must come before all global declarations");
     auto program = p->program();
     auto& ast = program.AST();
-    // Accept the enable directive although it cause an error
-    EXPECT_EQ(ast.Extensions(),
-              ast::ExtensionSet{ast::Enable::ExtensionKind::kInternalExtensionForTesting});
-    EXPECT_EQ(ast.GlobalDeclarations().size(), 2u);
+    // Accept the enable directive although it caused an error
+    ASSERT_EQ(ast.Enables().size(), 1u);
+    auto* enable = ast.Enables()[0];
+    EXPECT_EQ(enable->extension, ast::Extension::kF16);
+    ASSERT_EQ(ast.GlobalDeclarations().size(), 2u);
+    EXPECT_EQ(ast.GlobalDeclarations()[1], enable);
 }
 
-// Test an enable directive go after an empty semiclon.
-TEST_F(EnableDirectiveTest, FollowingEmptySemiclon) {
+// Test an enable directive go after an empty semicolon.
+TEST_F(EnableDirectiveTest, FollowingEmptySemicolon) {
     auto p = parser(R"(
 ;
-enable InternalExtensionForTesting;
+enable f16;
 )");
     p->translation_unit();
-    // An empty semiclon is treated as a global declaration
+    // An empty semicolon is treated as a global declaration
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), "3:1: enable directives must come before all global declarations");
     auto program = p->program();
     auto& ast = program.AST();
     // Accept the enable directive although it cause an error
-    EXPECT_EQ(ast.Extensions(),
-              ast::ExtensionSet{ast::Enable::ExtensionKind::kInternalExtensionForTesting});
-    EXPECT_EQ(ast.GlobalDeclarations().size(), 1u);
+    ASSERT_EQ(ast.Enables().size(), 1u);
+    auto* enable = ast.Enables()[0];
+    EXPECT_EQ(enable->extension, ast::Extension::kF16);
+    ASSERT_EQ(ast.GlobalDeclarations().size(), 1u);
+    EXPECT_EQ(ast.GlobalDeclarations()[0], enable);
 }
 
 }  // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_equality_expression_test.cc b/src/tint/reader/wgsl/parser_impl_equality_expression_test.cc
index 4e1f5a3..158227d 100644
--- a/src/tint/reader/wgsl/parser_impl_equality_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_equality_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 5u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kEqual, rel->op);
@@ -45,6 +50,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 5u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kNotEqual, rel->op);
diff --git a/src/tint/reader/wgsl/parser_impl_exclusive_or_expression_test.cc b/src/tint/reader/wgsl/parser_impl_exclusive_or_expression_test.cc
index c6f8ad4..2994ae8 100644
--- a/src/tint/reader/wgsl/parser_impl_exclusive_or_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_exclusive_or_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 4u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kXor, rel->op);
diff --git a/src/tint/reader/wgsl/parser_impl_inclusive_or_expression_test.cc b/src/tint/reader/wgsl/parser_impl_inclusive_or_expression_test.cc
index 83b9358..f534ff7 100644
--- a/src/tint/reader/wgsl/parser_impl_inclusive_or_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_inclusive_or_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 4u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kOr, rel->op);
diff --git a/src/tint/reader/wgsl/parser_impl_logical_and_expression_test.cc b/src/tint/reader/wgsl/parser_impl_logical_and_expression_test.cc
index 57624a5..8baadaf 100644
--- a/src/tint/reader/wgsl/parser_impl_logical_and_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_logical_and_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 5u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kLogicalAnd, rel->op);
diff --git a/src/tint/reader/wgsl/parser_impl_logical_or_expression_test.cc b/src/tint/reader/wgsl/parser_impl_logical_or_expression_test.cc
index 6bde6bd..943b059 100644
--- a/src/tint/reader/wgsl/parser_impl_logical_or_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_logical_or_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 5u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kLogicalOr, rel->op);
diff --git a/src/tint/reader/wgsl/parser_impl_multiplicative_expression_test.cc b/src/tint/reader/wgsl/parser_impl_multiplicative_expression_test.cc
index 6d77fd5..28ac568 100644
--- a/src/tint/reader/wgsl/parser_impl_multiplicative_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_multiplicative_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 4u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kMultiply, rel->op);
diff --git a/src/tint/reader/wgsl/parser_impl_relational_expression_test.cc b/src/tint/reader/wgsl/parser_impl_relational_expression_test.cc
index 725761b..7ad161f 100644
--- a/src/tint/reader/wgsl/parser_impl_relational_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_relational_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 4u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kLessThan, rel->op);
@@ -45,6 +50,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 4u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kGreaterThan, rel->op);
@@ -65,6 +75,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 5u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kLessThanEqual, rel->op);
@@ -85,6 +100,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 5u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kGreaterThanEqual, rel->op);
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 3226ef5..e840af7 100644
--- a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
@@ -88,7 +88,6 @@
                                          "const",
                                          "do",
                                          "enum",
-                                         "f16",
                                          "f64",
                                          "handle",
                                          "i8",
diff --git a/src/tint/reader/wgsl/parser_impl_shift_expression_test.cc b/src/tint/reader/wgsl/parser_impl_shift_expression_test.cc
index fd612b5..83c1255 100644
--- a/src/tint/reader/wgsl/parser_impl_shift_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_shift_expression_test.cc
@@ -25,6 +25,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 5u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kShiftLeft, rel->op);
@@ -45,6 +50,11 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     ASSERT_NE(e.value, nullptr);
 
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 3u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 5u);
+
     ASSERT_TRUE(e->Is<ast::BinaryExpression>());
     auto* rel = e->As<ast::BinaryExpression>();
     EXPECT_EQ(ast::BinaryOp::kShiftRight, rel->op);
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
index a721d77..bc2bfe5 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
@@ -55,6 +55,17 @@
     EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 5u}}));
 }
 
+TEST_F(ParserImplTest, TypeDecl_F16) {
+    auto p = parser("f16");
+
+    auto t = p->type_decl();
+    EXPECT_TRUE(t.matched);
+    EXPECT_FALSE(t.errored);
+    ASSERT_NE(t.value, nullptr) << p->error();
+    ASSERT_TRUE(t.value->Is<ast::F16>());
+    EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 4u}}));
+}
+
 TEST_F(ParserImplTest, TypeDecl_F32) {
     auto p = parser("f32");
 
diff --git a/src/tint/reader/wgsl/token.cc b/src/tint/reader/wgsl/token.cc
index 06eb6fb..4680eee 100644
--- a/src/tint/reader/wgsl/token.cc
+++ b/src/tint/reader/wgsl/token.cc
@@ -151,6 +151,8 @@
             return "else";
         case Token::Type::kEnable:
             return "enable";
+        case Token::Type::kF16:
+            return "f16";
         case Token::Type::kF32:
             return "f32";
         case Token::Type::kFallthrough:
diff --git a/src/tint/reader/wgsl/token.h b/src/tint/reader/wgsl/token.h
index 82b3fab..0a68f9b0 100644
--- a/src/tint/reader/wgsl/token.h
+++ b/src/tint/reader/wgsl/token.h
@@ -162,6 +162,8 @@
         kElse,
         /// A 'enable'
         kEnable,
+        /// A 'f16'
+        kF16,
         /// A 'f32'
         kF32,
         /// A 'fallthrough'
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index 6020eae..74a898c 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -1924,7 +1924,7 @@
         }
     }
 
-    auto* call_sem = Sem().Get(call);
+    auto* call_sem = Sem().Get<sem::Call>(call);
     ASSERT_NE(call_sem, nullptr);
     auto* target = call_sem->Target();
     ASSERT_NE(target, nullptr);
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index 0e48b55..770d8d0 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -378,10 +378,7 @@
 TEST_F(ResolverDP4aExtensionValidationTest, Dot4I8PackedWithExtension) {
     // enable chromium_experimental_dp4a;
     // fn func { return dot4I8Packed(1u, 2u); }
-    auto* ext =
-        create<ast::Enable>(Source{Source::Range{Source::Location{10, 2}, Source::Location{10, 5}}},
-                            "chromium_experimental_dp4a");
-    AST().AddEnable(ext);
+    Enable(ast::Extension::kChromiumExperimentalDP4a);
 
     Func("func", {}, ty.i32(),
          {
@@ -409,10 +406,7 @@
 TEST_F(ResolverDP4aExtensionValidationTest, Dot4U8PackedWithExtension) {
     // enable chromium_experimental_dp4a;
     // fn func { return dot4U8Packed(1u, 2u); }
-    auto* ext =
-        create<ast::Enable>(Source{Source::Range{Source::Location{10, 2}, Source::Location{10, 5}}},
-                            "chromium_experimental_dp4a");
-    AST().AddEnable(ext);
+    Enable(ast::Extension::kChromiumExperimentalDP4a);
 
     Func("func", {}, ty.u32(),
          {
diff --git a/src/tint/resolver/call_test.cc b/src/tint/resolver/call_test.cc
index d84f300..39a6eb4 100644
--- a/src/tint/resolver/call_test.cc
+++ b/src/tint/resolver/call_test.cc
@@ -94,7 +94,7 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    auto* call = Sem().Get(call_expr);
+    auto* call = Sem().Get<sem::Call>(call_expr);
     EXPECT_NE(call, nullptr);
     EXPECT_EQ(call->Target(), Sem().Get(func));
 }
@@ -106,7 +106,7 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    auto* call = Sem().Get(call_expr);
+    auto* call = Sem().Get<sem::Call>(call_expr);
     EXPECT_NE(call, nullptr);
     EXPECT_EQ(call->Target(), Sem().Get(b));
 }
diff --git a/src/tint/resolver/compound_statement_test.cc b/src/tint/resolver/compound_statement_test.cc
index 4061a62..0444bd3 100644
--- a/src/tint/resolver/compound_statement_test.cc
+++ b/src/tint/resolver/compound_statement_test.cc
@@ -144,6 +144,35 @@
     }
 }
 
+TEST_F(ResolverCompoundStatementTest, Loop_EmptyContinuing) {
+    // fn F() {
+    //   loop {
+    //     break;
+    //     continuing {
+    //     }
+    //   }
+    // }
+    auto* brk = Break();
+    auto* loop = Loop(Block(brk), Block());
+    Func("F", {}, ty.void_(), {loop});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    {
+        auto* s = Sem().Get(loop);
+        ASSERT_NE(s, nullptr);
+        EXPECT_TRUE(s->Is<sem::LoopStatement>());
+        EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+        EXPECT_EQ(s->Parent(), s->Block());
+    }
+    {
+        auto* s = Sem().Get(loop->continuing);
+        ASSERT_NE(s, nullptr);
+        EXPECT_TRUE(Is<sem::LoopContinuingBlockStatement>(s));
+        EXPECT_TRUE(Is<sem::LoopStatement>(s->Parent()->Parent()));
+    }
+}
+
 TEST_F(ResolverCompoundStatementTest, ForLoop) {
     // fn F() {
     //   for (var i : u32; true; i = i + 1u) {
diff --git a/src/tint/resolver/ctor_conv_intrinsic.cc b/src/tint/resolver/ctor_conv_intrinsic.cc
new file mode 100644
index 0000000..5618fba
--- /dev/null
+++ b/src/tint/resolver/ctor_conv_intrinsic.cc
@@ -0,0 +1,70 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/intrinsic-gen
+// using the template:
+//   src/tint/resolver/ctor_conv_intrinsic.cc.tmpl
+// and the intrinsic defintion file:
+//   src/tint/intrinsics.def
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/resolver/ctor_conv_intrinsic.h"
+
+namespace tint::resolver {
+
+const char* str(CtorConvIntrinsic i) {
+    switch (i) {
+        case CtorConvIntrinsic::kNone:
+            return "<none>";
+        case CtorConvIntrinsic::kI32:
+            return "i32";
+        case CtorConvIntrinsic::kU32:
+            return "u32";
+        case CtorConvIntrinsic::kF32:
+            return "f32";
+        case CtorConvIntrinsic::kBool:
+            return "bool";
+        case CtorConvIntrinsic::kVec2:
+            return "vec2";
+        case CtorConvIntrinsic::kVec3:
+            return "vec3";
+        case CtorConvIntrinsic::kVec4:
+            return "vec4";
+        case CtorConvIntrinsic::kMat2x2:
+            return "mat2x2";
+        case CtorConvIntrinsic::kMat2x3:
+            return "mat2x3";
+        case CtorConvIntrinsic::kMat2x4:
+            return "mat2x4";
+        case CtorConvIntrinsic::kMat3x2:
+            return "mat3x2";
+        case CtorConvIntrinsic::kMat3x3:
+            return "mat3x3";
+        case CtorConvIntrinsic::kMat3x4:
+            return "mat3x4";
+        case CtorConvIntrinsic::kMat4x2:
+            return "mat4x2";
+        case CtorConvIntrinsic::kMat4x3:
+            return "mat4x3";
+        case CtorConvIntrinsic::kMat4x4:
+            return "mat4x4";
+    }
+    return "<unknown>";
+}
+
+}  // namespace tint::resolver
+
diff --git a/src/tint/resolver/ctor_conv_intrinsic.cc.tmpl b/src/tint/resolver/ctor_conv_intrinsic.cc.tmpl
new file mode 100644
index 0000000..ac98c4d
--- /dev/null
+++ b/src/tint/resolver/ctor_conv_intrinsic.cc.tmpl
@@ -0,0 +1,28 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/builtin-gen to generate ctor_conv_intrinsic.cc
+
+See:
+* tools/cmd/intrinsic-gen/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+#include "src/tint/resolver/ctor_conv_intrinsic.h"
+
+namespace tint::resolver {
+
+const char* str(CtorConvIntrinsic i) {
+    switch (i) {
+        case CtorConvIntrinsic::kNone:
+            return "<none>";
+{{- range .Sem.ConstructorsAndConverters  }}
+        case CtorConvIntrinsic::k{{Title .Name}}:
+            return "{{.Name}}";
+{{- end  }}
+    }
+    return "<unknown>";
+}
+
+}  // namespace tint::resolver
+
diff --git a/src/tint/resolver/ctor_conv_intrinsic.h b/src/tint/resolver/ctor_conv_intrinsic.h
new file mode 100644
index 0000000..7c68658
--- /dev/null
+++ b/src/tint/resolver/ctor_conv_intrinsic.h
@@ -0,0 +1,100 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/intrinsic-gen
+// using the template:
+//   src/tint/resolver/ctor_conv_intrinsic.h.tmpl
+// and the intrinsic defintion file:
+//   src/tint/intrinsics.def
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SRC_TINT_RESOLVER_CTOR_CONV_INTRINSIC_H_
+#define SRC_TINT_RESOLVER_CTOR_CONV_INTRINSIC_H_
+
+#include <cstdint>
+
+namespace tint::resolver {
+
+/// CtorConvIntrinsic is an enumerator of types that have a constructor or converter overload
+/// declared in the intrinsic table.
+enum class CtorConvIntrinsic {
+    kNone = -1,
+    kI32,
+    kU32,
+    kF32,
+    kBool,
+    kVec2,
+    kVec3,
+    kVec4,
+    kMat2x2,
+    kMat2x3,
+    kMat2x4,
+    kMat3x2,
+    kMat3x3,
+    kMat3x4,
+    kMat4x2,
+    kMat4x3,
+    kMat4x4,
+};
+
+/// @returns the name of the type.
+const char* str(CtorConvIntrinsic i);
+
+/// @param n the width of the vector
+/// @return the CtorConvIntrinsic for a vector of width `n`
+inline CtorConvIntrinsic VectorCtorConvIntrinsic(uint32_t n) {
+    switch (n) {
+        case 2:
+            return CtorConvIntrinsic::kVec2;
+        case 3:
+            return CtorConvIntrinsic::kVec3;
+        case 4:
+            return CtorConvIntrinsic::kVec4;
+    }
+    return CtorConvIntrinsic::kNone;
+}
+
+/// @param c the number of columns in the matrix
+/// @param r the number of rows in the matrix
+/// @return the CtorConvIntrinsic for a matrix with `c` columns and `r` rows
+inline CtorConvIntrinsic MatrixCtorConvIntrinsic(uint32_t c, uint32_t r) {
+    switch ((c - 2) * 3 + (r - 2)) {
+        case 0:
+            return CtorConvIntrinsic::kMat2x2;
+        case 1:
+            return CtorConvIntrinsic::kMat2x3;
+        case 2:
+            return CtorConvIntrinsic::kMat2x4;
+        case 3:
+            return CtorConvIntrinsic::kMat3x2;
+        case 4:
+            return CtorConvIntrinsic::kMat3x3;
+        case 5:
+            return CtorConvIntrinsic::kMat3x4;
+        case 6:
+            return CtorConvIntrinsic::kMat4x2;
+        case 7:
+            return CtorConvIntrinsic::kMat4x3;
+        case 8:
+            return CtorConvIntrinsic::kMat4x4;
+    }
+    return CtorConvIntrinsic::kNone;
+}
+
+}  // namespace tint::resolver
+
+#endif  // SRC_TINT_RESOLVER_CTOR_CONV_INTRINSIC_H_
diff --git a/src/tint/resolver/ctor_conv_intrinsic.h.tmpl b/src/tint/resolver/ctor_conv_intrinsic.h.tmpl
new file mode 100644
index 0000000..9c0da25
--- /dev/null
+++ b/src/tint/resolver/ctor_conv_intrinsic.h.tmpl
@@ -0,0 +1,73 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/builtin-gen to generate ctor_conv_intrinsic.h
+
+See:
+* tools/cmd/intrinsic-gen/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+#ifndef SRC_TINT_RESOLVER_CTOR_CONV_INTRINSIC_H_
+#define SRC_TINT_RESOLVER_CTOR_CONV_INTRINSIC_H_
+
+#include <cstdint>
+
+namespace tint::resolver {
+
+/// CtorConvIntrinsic is an enumerator of types that have a constructor or converter overload
+/// declared in the intrinsic table.
+enum class CtorConvIntrinsic {
+    kNone = -1,
+{{- range .Sem.ConstructorsAndConverters }}
+    k{{Title .Name}},
+{{- end }}
+};
+
+/// @returns the name of the type.
+const char* str(CtorConvIntrinsic i);
+
+/// @param n the width of the vector
+/// @return the CtorConvIntrinsic for a vector of width `n`
+inline CtorConvIntrinsic VectorCtorConvIntrinsic(uint32_t n) {
+    switch (n) {
+        case 2:
+            return CtorConvIntrinsic::kVec2;
+        case 3:
+            return CtorConvIntrinsic::kVec3;
+        case 4:
+            return CtorConvIntrinsic::kVec4;
+    }
+    return CtorConvIntrinsic::kNone;
+}
+
+/// @param c the number of columns in the matrix
+/// @param r the number of rows in the matrix
+/// @return the CtorConvIntrinsic for a matrix with `c` columns and `r` rows
+inline CtorConvIntrinsic MatrixCtorConvIntrinsic(uint32_t c, uint32_t r) {
+    switch ((c - 2) * 3 + (r - 2)) {
+        case 0:
+            return CtorConvIntrinsic::kMat2x2;
+        case 1:
+            return CtorConvIntrinsic::kMat2x3;
+        case 2:
+            return CtorConvIntrinsic::kMat2x4;
+        case 3:
+            return CtorConvIntrinsic::kMat3x2;
+        case 4:
+            return CtorConvIntrinsic::kMat3x3;
+        case 5:
+            return CtorConvIntrinsic::kMat3x4;
+        case 6:
+            return CtorConvIntrinsic::kMat4x2;
+        case 7:
+            return CtorConvIntrinsic::kMat4x3;
+        case 8:
+            return CtorConvIntrinsic::kMat4x4;
+    }
+    return CtorConvIntrinsic::kNone;
+}
+
+}  // namespace tint::resolver
+
+#endif  // SRC_TINT_RESOLVER_CTOR_CONV_INTRINSIC_H_
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 0a9bdc0..7e66899 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -342,7 +342,7 @@
                 TraverseType(tex->type);
             },
             [&](Default) {
-                if (!ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F32,
+                if (!ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F16, ast::F32,
                                  ast::DepthTexture, ast::DepthMultisampledTexture,
                                  ast::StorageTexture, ast::ExternalTexture, ast::Sampler>()) {
                     UnhandledNode(diagnostics_, ty);
@@ -420,7 +420,7 @@
     DependencyGraph& graph_;
     DependencyEdges& dependency_edges_;
 
-    ScopeStack<const ast::Node*> scope_stack_;
+    ScopeStack<Symbol, const ast::Node*> scope_stack_;
     Global* current_global_ = nullptr;
 };
 
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index fa9dbf8..6c19f7c 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -20,6 +20,9 @@
 #include <utility>
 
 #include "src/tint/program_builder.h"
+#include "src/tint/sem/abstract_float.h"
+#include "src/tint/sem/abstract_int.h"
+#include "src/tint/sem/abstract_numeric.h"
 #include "src/tint/sem/atomic.h"
 #include "src/tint/sem/depth_multisampled_texture.h"
 #include "src/tint/sem/depth_texture.h"
@@ -28,12 +31,14 @@
 #include "src/tint/sem/pipeline_stage_set.h"
 #include "src/tint/sem/sampled_texture.h"
 #include "src/tint/sem/storage_texture.h"
+#include "src/tint/sem/type_constructor.h"
+#include "src/tint/sem/type_conversion.h"
 #include "src/tint/utils/hash.h"
 #include "src/tint/utils/map.h"
 #include "src/tint/utils/math.h"
 #include "src/tint/utils/scoped_assignment.h"
 
-namespace tint {
+namespace tint::resolver {
 namespace {
 
 // Forward declarations
@@ -98,83 +103,86 @@
 const Number Number::any{Number::kAny};
 const Number Number::invalid{Number::kInvalid};
 
-/// ClosedState holds the state of the open / closed numbers and types.
+/// TemplateState holds the state of the template numbers and types.
 /// Used by the MatchState.
-class ClosedState {
+class TemplateState {
   public:
-    explicit ClosedState(ProgramBuilder& b) : builder(b) {}
-
-    /// If the type with index `idx` is open, then it is closed with type `ty` and
-    /// Type() returns true. If the type is closed, then `Type()` returns true iff
-    /// it is equal to `ty`.
-    bool Type(uint32_t idx, const sem::Type* ty) {
+    /// If the template type with index `idx` is undefined, then it is defined with the `ty` and
+    /// Type() returns `ty`.
+    /// If the template type is defined, and `ty` can be converted to the template type then the
+    /// template type is returned.
+    /// If the template type is defined, and the template type can be converted to `ty`, then the
+    /// template type is replaced with `ty`, and `ty` is returned.
+    /// If none of the above applies, then `ty` is a type mismatch for the template type, and
+    /// nullptr is returned.
+    const sem::Type* Type(size_t idx, const sem::Type* ty) {
         auto res = types_.emplace(idx, ty);
-        return res.second || res.first->second == ty;
+        if (res.second) {
+            return ty;
+        }
+        auto* existing = res.first->second;
+        if (existing == ty) {
+            return ty;
+        }
+        ty = sem::Type::Common({existing, ty});
+        if (ty) {
+            res.first->second = ty;
+        }
+        return ty;
     }
 
-    /// If the number with index `idx` is open, then it is closed with number
-    /// `number` and Num() returns true. If the number is closed, then `Num()`
-    /// returns true iff it is equal to `ty`.
-    bool Num(uint32_t idx, Number number) {
+    /// If the number with index `idx` is undefined, then it is defined with the number `number` and
+    /// Num() returns true. If the number is defined, then `Num()` returns true iff it is equal to
+    /// `ty`.
+    bool Num(size_t idx, Number number) {
         auto res = numbers_.emplace(idx, number.Value());
         return res.second || res.first->second == number.Value();
     }
 
-    /// Type returns the closed type with index `idx`.
-    /// An ICE is raised if the type is not closed.
-    const sem::Type* Type(uint32_t idx) const {
+    /// Type returns the template type with index `idx`, or nullptr if the type was not defined.
+    const sem::Type* Type(size_t idx) const {
         auto it = types_.find(idx);
-        if (it == types_.end()) {
-            TINT_ICE(Resolver, builder.Diagnostics())
-                << "type with index " << idx << " is not closed";
-            return nullptr;
-        }
-        TINT_ASSERT(Resolver, it != types_.end());
-        return it->second;
+        return (it != types_.end()) ? it->second : nullptr;
     }
 
+    /// SetType replaces the template type with index `idx` with type `ty`.
+    void SetType(size_t idx, const sem::Type* ty) { types_[idx] = ty; }
+
     /// Type returns the number type with index `idx`.
-    /// An ICE is raised if the number is not closed.
-    Number Num(uint32_t idx) const {
+    Number Num(size_t idx) const {
         auto it = numbers_.find(idx);
-        if (it == numbers_.end()) {
-            TINT_ICE(Resolver, builder.Diagnostics())
-                << "number with index " << idx << " is not closed";
-            return Number::invalid;
-        }
-        return Number(it->second);
+        return (it != numbers_.end()) ? Number(it->second) : Number::invalid;
     }
 
   private:
-    ProgramBuilder& builder;
-    std::unordered_map<uint32_t, const sem::Type*> types_;
-    std::unordered_map<uint32_t, uint32_t> numbers_;
+    std::unordered_map<size_t, const sem::Type*> types_;
+    std::unordered_map<size_t, uint32_t> numbers_;
 };
 
 /// Index type used for matcher indices
 using MatcherIndex = uint8_t;
 
-/// Index value used for open types / numbers that do not have a constraint
+/// Index value used for template types / numbers that do not have a constraint
 constexpr MatcherIndex kNoMatcher = std::numeric_limits<MatcherIndex>::max();
 
 /// MatchState holds the state used to match an overload.
 class MatchState {
   public:
     MatchState(ProgramBuilder& b,
-               ClosedState& c,
+               TemplateState& t,
                const Matchers& m,
-               const OverloadInfo& o,
+               const OverloadInfo* o,
                MatcherIndex const* matcher_indices)
-        : builder(b), closed(c), matchers(m), overload(o), matcher_indices_(matcher_indices) {}
+        : builder(b), templates(t), matchers(m), overload(o), matcher_indices_(matcher_indices) {}
 
     /// The program builder
     ProgramBuilder& builder;
-    /// The open / closed types and numbers
-    ClosedState& closed;
+    /// The template types and numbers
+    TemplateState& templates;
     /// The type and number matchers
     Matchers const& matchers;
     /// The current overload being evaluated
-    OverloadInfo const& overload;
+    OverloadInfo const* overload;
 
     /// Type uses the next TypeMatcher from the matcher indices to match the type
     /// `ty`. If the type matches, the canonical expected type is returned. If the
@@ -211,14 +219,14 @@
 
     /// Checks whether the given type matches the matcher rules, and returns the
     /// expected, canonicalized type on success.
-    /// Match may close open types and numbers in state.
+    /// Match may define and refine the template types and numbers in state.
     /// @param type the type to match
     /// @returns the canonicalized type on match, otherwise nullptr
     virtual const sem::Type* Match(MatchState& state, const sem::Type* type) const = 0;
 
     /// @return a string representation of the matcher. Used for printing error
     /// messages when no overload is found.
-    virtual std::string String(MatchState& state) const = 0;
+    virtual std::string String(MatchState* state) const = 0;
 };
 
 /// A NumberMatcher is the interface used to match a number or enumerator used
@@ -229,55 +237,58 @@
     virtual ~NumberMatcher() = default;
 
     /// Checks whether the given number matches the matcher rules.
-    /// Match may close open numbers in state.
+    /// Match may define template numbers in state.
     /// @param number the number to match
     /// @returns true if the argument type is as expected.
     virtual Number Match(MatchState& state, Number number) const = 0;
 
     /// @return a string representation of the matcher. Used for printing error
     /// messages when no overload is found.
-    virtual std::string String(MatchState& state) const = 0;
+    virtual std::string String(MatchState* state) const = 0;
 };
 
-/// OpenTypeMatcher is a Matcher for an open type.
-/// The OpenTypeMatcher will match against any type (so long as it is consistent
-/// across all uses in the overload)
-class OpenTypeMatcher : public TypeMatcher {
+/// TemplateTypeMatcher is a Matcher for a template type.
+/// The TemplateTypeMatcher will initially match against any type, and then will only be further
+/// constrained based on the conversion rules defined at https://www.w3.org/TR/WGSL/#conversion-rank
+class TemplateTypeMatcher : public TypeMatcher {
   public:
     /// Constructor
-    explicit OpenTypeMatcher(uint32_t index) : index_(index) {}
+    explicit TemplateTypeMatcher(size_t index) : index_(index) {}
 
     const sem::Type* Match(MatchState& state, const sem::Type* type) const override {
         if (type->Is<Any>()) {
-            return state.closed.Type(index_);
+            return state.templates.Type(index_);
         }
-        return state.closed.Type(index_, type) ? type : nullptr;
+        if (auto* templates = state.templates.Type(index_, type)) {
+            return templates;
+        }
+        return nullptr;
     }
 
-    std::string String(MatchState& state) const override;
+    std::string String(MatchState* state) const override;
 
   private:
-    uint32_t index_;
+    size_t index_;
 };
 
-/// OpenNumberMatcher is a Matcher for an open number.
-/// The OpenNumberMatcher will match against any number (so long as it is
-/// consistent for the overload)
-class OpenNumberMatcher : public NumberMatcher {
+/// TemplateNumberMatcher is a Matcher for a template number.
+/// The TemplateNumberMatcher will match against any number (so long as it is
+/// consistent for all uses in the overload)
+class TemplateNumberMatcher : public NumberMatcher {
   public:
-    explicit OpenNumberMatcher(uint32_t index) : index_(index) {}
+    explicit TemplateNumberMatcher(size_t index) : index_(index) {}
 
     Number Match(MatchState& state, Number number) const override {
         if (number.IsAny()) {
-            return state.closed.Num(index_);
+            return state.templates.Num(index_);
         }
-        return state.closed.Num(index_, number) ? number : Number::invalid;
+        return state.templates.Num(index_, number) ? number : Number::invalid;
     }
 
-    std::string String(MatchState& state) const override;
+    std::string String(MatchState* state) const override;
 
   private:
-    uint32_t index_;
+    size_t index_;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -289,19 +300,53 @@
 using Access = ast::Access;
 using StorageClass = ast::StorageClass;
 using ParameterUsage = sem::ParameterUsage;
-using PipelineStageSet = sem::PipelineStageSet;
 using PipelineStage = ast::PipelineStage;
 
+/// Unique flag bits for overloads
+enum class OverloadFlag {
+    kIsBuiltin,                 // The overload is a builtin ('fn')
+    kIsOperator,                // The overload is an operator ('op')
+    kIsConstructor,             // The overload is a type constructor ('ctor')
+    kIsConverter,               // The overload is a type converter ('conv')
+    kSupportsVertexPipeline,    // The overload can be used in vertex shaders
+    kSupportsFragmentPipeline,  // The overload can be used in fragment shaders
+    kSupportsComputePipeline,   // The overload can be used in compute shaders
+    kIsDeprecated,              // The overload is deprecated
+};
+
+// An enum set of OverloadFlag, used by OperatorInfo
+using OverloadFlags = utils::EnumSet<OverloadFlag>;
+
 bool match_bool(const sem::Type* ty) {
     return ty->IsAnyOf<Any, sem::Bool>();
 }
 
+const sem::AbstractFloat* build_af(MatchState& state) {
+    return state.builder.create<sem::AbstractFloat>();
+}
+
+bool match_af(const sem::Type* ty) {
+    return ty->IsAnyOf<Any, sem::AbstractFloat>();
+}
+
+const sem::AbstractInt* build_ai(MatchState& state) {
+    return state.builder.create<sem::AbstractInt>();
+}
+
+bool match_ai(const sem::Type* ty) {
+    return ty->IsAnyOf<Any, sem::AbstractInt>();
+}
+
 const sem::Bool* build_bool(MatchState& state) {
     return state.builder.create<sem::Bool>();
 }
 
+const sem::F32* build_f32(MatchState& state) {
+    return state.builder.create<sem::F32>();
+}
+
 bool match_f32(const sem::Type* ty) {
-    return ty->IsAnyOf<Any, sem::F32>();
+    return ty->IsAnyOf<Any, sem::F32, sem::AbstractNumeric>();
 }
 
 const sem::I32* build_i32(MatchState& state) {
@@ -309,7 +354,7 @@
 }
 
 bool match_i32(const sem::Type* ty) {
-    return ty->IsAnyOf<Any, sem::I32>();
+    return ty->IsAnyOf<Any, sem::I32, sem::AbstractInt>();
 }
 
 const sem::U32* build_u32(MatchState& state) {
@@ -317,11 +362,7 @@
 }
 
 bool match_u32(const sem::Type* ty) {
-    return ty->IsAnyOf<Any, sem::U32>();
-}
-
-const sem::F32* build_f32(MatchState& state) {
-    return state.builder.create<sem::F32>();
+    return ty->IsAnyOf<Any, sem::U32, sem::AbstractInt>();
 }
 
 bool match_vec(const sem::Type* ty, Number& N, const sem::Type*& T) {
@@ -339,11 +380,7 @@
     return false;
 }
 
-const sem::Vector* build_vec(MatchState& state, Number N, const sem::Type* el) {
-    return state.builder.create<sem::Vector>(el, N.Value());
-}
-
-template <int N>
+template <uint32_t N>
 bool match_vec(const sem::Type* ty, const sem::Type*& T) {
     if (ty->Is<Any>()) {
         T = ty;
@@ -359,29 +396,22 @@
     return false;
 }
 
-bool match_vec2(const sem::Type* ty, const sem::Type*& T) {
-    return match_vec<2>(ty, T);
+const sem::Vector* build_vec(MatchState& state, Number N, const sem::Type* el) {
+    return state.builder.create<sem::Vector>(el, N.Value());
 }
 
-const sem::Vector* build_vec2(MatchState& state, const sem::Type* T) {
-    return build_vec(state, Number(2), T);
+template <uint32_t N>
+const sem::Vector* build_vec(MatchState& state, const sem::Type* el) {
+    return state.builder.create<sem::Vector>(el, N);
 }
 
-bool match_vec3(const sem::Type* ty, const sem::Type*& T) {
-    return match_vec<3>(ty, T);
-}
+constexpr auto match_vec2 = match_vec<2>;
+constexpr auto match_vec3 = match_vec<3>;
+constexpr auto match_vec4 = match_vec<4>;
 
-const sem::Vector* build_vec3(MatchState& state, const sem::Type* T) {
-    return build_vec(state, Number(3), T);
-}
-
-bool match_vec4(const sem::Type* ty, const sem::Type*& T) {
-    return match_vec<4>(ty, T);
-}
-
-const sem::Vector* build_vec4(MatchState& state, const sem::Type* T) {
-    return build_vec(state, Number(4), T);
-}
+constexpr auto build_vec2 = build_vec<2>;
+constexpr auto build_vec3 = build_vec<3>;
+constexpr auto build_vec4 = build_vec<4>;
 
 bool match_mat(const sem::Type* ty, Number& M, Number& N, const sem::Type*& T) {
     if (ty->Is<Any>()) {
@@ -399,11 +429,52 @@
     return false;
 }
 
-const sem::Matrix* build_mat(MatchState& state, Number N, Number M, const sem::Type* T) {
-    auto* column_type = state.builder.create<sem::Vector>(T, M.Value());
-    return state.builder.create<sem::Matrix>(column_type, N.Value());
+template <uint32_t C, uint32_t R>
+bool match_mat(const sem::Type* ty, const sem::Type*& T) {
+    if (ty->Is<Any>()) {
+        T = ty;
+        return true;
+    }
+    if (auto* m = ty->As<sem::Matrix>()) {
+        if (m->columns() == C && m->rows() == R) {
+            T = m->type();
+            return true;
+        }
+    }
+    return false;
 }
 
+const sem::Matrix* build_mat(MatchState& state, Number C, Number R, const sem::Type* T) {
+    auto* column_type = state.builder.create<sem::Vector>(T, R.Value());
+    return state.builder.create<sem::Matrix>(column_type, C.Value());
+}
+
+template <uint32_t C, uint32_t R>
+const sem::Matrix* build_mat(MatchState& state, const sem::Type* T) {
+    auto* column_type = state.builder.create<sem::Vector>(T, R);
+    return state.builder.create<sem::Matrix>(column_type, C);
+}
+
+constexpr auto build_mat2x2 = build_mat<2, 2>;
+constexpr auto build_mat2x3 = build_mat<2, 3>;
+constexpr auto build_mat2x4 = build_mat<2, 4>;
+constexpr auto build_mat3x2 = build_mat<3, 2>;
+constexpr auto build_mat3x3 = build_mat<3, 3>;
+constexpr auto build_mat3x4 = build_mat<3, 4>;
+constexpr auto build_mat4x2 = build_mat<4, 2>;
+constexpr auto build_mat4x3 = build_mat<4, 3>;
+constexpr auto build_mat4x4 = build_mat<4, 4>;
+
+constexpr auto match_mat2x2 = match_mat<2, 2>;
+constexpr auto match_mat2x3 = match_mat<2, 3>;
+constexpr auto match_mat2x4 = match_mat<2, 4>;
+constexpr auto match_mat3x2 = match_mat<3, 2>;
+constexpr auto match_mat3x3 = match_mat<3, 3>;
+constexpr auto match_mat3x4 = match_mat<3, 4>;
+constexpr auto match_mat4x2 = match_mat<4, 2>;
+constexpr auto match_mat4x3 = match_mat<4, 3>;
+constexpr auto match_mat4x4 = match_mat<4, 4>;
+
 bool match_array(const sem::Type* ty, const sem::Type*& T) {
     if (ty->Is<Any>()) {
         T = ty;
@@ -720,18 +791,18 @@
     MatcherIndex const* const matcher_indices;
 };
 
-/// OpenTypeInfo describes an open type
-struct OpenTypeInfo {
-    /// Name of the open type (e.g. 'T')
+/// TemplateTypeInfo describes an template type
+struct TemplateTypeInfo {
+    /// Name of the template type (e.g. 'T')
     const char* name;
     /// Optional type matcher constraint.
     /// Either an index in Matchers::type, or kNoMatcher
     const MatcherIndex matcher_index;
 };
 
-/// OpenNumberInfo describes an open number
-struct OpenNumberInfo {
-    /// Name of the open number (e.g. 'N')
+/// TemplateNumberInfo describes a template number
+struct TemplateNumberInfo {
+    /// Name of the template number (e.g. 'N')
     const char* name;
     /// Optional number matcher constraint.
     /// Either an index in Matchers::number, or kNoMatcher
@@ -742,24 +813,22 @@
 struct OverloadInfo {
     /// Total number of parameters for the overload
     const uint8_t num_parameters;
-    /// Total number of open types for the overload
-    const uint8_t num_open_types;
-    /// Total number of open numbers for the overload
-    const uint8_t num_open_numbers;
-    /// Pointer to the first open type
-    OpenTypeInfo const* const open_types;
-    /// Pointer to the first open number
-    OpenNumberInfo const* const open_numbers;
+    /// Total number of template types for the overload
+    const uint8_t num_template_types;
+    /// Total number of template numbers for the overload
+    const uint8_t num_template_numbers;
+    /// Pointer to the first template type
+    TemplateTypeInfo const* const template_types;
+    /// Pointer to the first template number
+    TemplateNumberInfo const* const template_numbers;
     /// Pointer to the first parameter
     ParameterInfo const* const parameters;
     /// Pointer to a list of matcher indices that index on Matchers::type and
     /// Matchers::number, used to build the return type. If the function has no
     /// return type then this is null
     MatcherIndex const* const return_matcher_indices;
-    /// The pipeline stages that this overload can be used in
-    PipelineStageSet supported_stages;
-    /// True if the overload is marked as deprecated
-    bool is_deprecated;
+    /// The flags for the overload
+    OverloadFlags flags;
 };
 
 /// IntrinsicInfo describes a builtin function or operator overload
@@ -791,21 +860,18 @@
             for (auto& p : i.parameters) {
                 utils::HashCombine(&hash, p.type, p.usage);
             }
-            return utils::Hash(hash, i.index, i.return_type, i.supported_stages, i.is_deprecated);
+            return utils::Hash(hash, i.overload, i.return_type);
         }
     };
 
-    uint32_t index = 0;  // Index of the intrinsic (builtin or operator)
-    std::vector<Parameter> parameters;
+    const OverloadInfo* overload = nullptr;
     sem::Type const* return_type = nullptr;
-    PipelineStageSet supported_stages;
-    bool is_deprecated = false;
+    std::vector<Parameter> parameters;
 };
 
 /// Equality operator for IntrinsicPrototype
 bool operator==(const IntrinsicPrototype& a, const IntrinsicPrototype& b) {
-    if (a.index != b.index || a.supported_stages != b.supported_stages ||
-        a.return_type != b.return_type || a.is_deprecated != b.is_deprecated ||
+    if (a.overload != b.overload || a.return_type != b.return_type ||
         a.parameters.size() != b.parameters.size()) {
         return false;
     }
@@ -836,38 +902,108 @@
                           const Source& source,
                           bool is_compound) override;
 
+    const sem::CallTarget* Lookup(CtorConvIntrinsic type,
+                                  const sem::Type* template_arg,
+                                  const std::vector<const sem::Type*>& args,
+                                  const Source& source) override;
+
   private:
-    // Candidate holds information about a mismatched overload that could be what the user intended
-    // to call.
+    /// Candidate holds information about an overload evaluated for resolution.
     struct Candidate {
+        /// The candidate overload
         const OverloadInfo* overload;
-        int score;
+        /// The template types and numbers
+        TemplateState templates;
+        /// The parameter types for the candidate overload
+        std::vector<IntrinsicPrototype::Parameter> parameters;
+        /// The match-score of the candidate overload.
+        /// A score of zero indicates an exact match.
+        /// Non-zero scores are used for diagnostics when no overload matches.
+        /// Lower scores are displayed first (top-most).
+        size_t score;
     };
 
-    const IntrinsicPrototype Match(const char* intrinsic_name,
-                                   uint32_t intrinsic_index,
-                                   const OverloadInfo& overload,
-                                   const std::vector<const sem::Type*>& args,
-                                   int& match_score);
+    /// A list of candidates
+    using Candidates = std::vector<Candidate>;
 
-    MatchState Match(ClosedState& closed,
-                     const OverloadInfo& overload,
+    /// Callback function when no overloads match.
+    using OnNoMatch = std::function<void(Candidates)>;
+
+    /// Attempts to find a single intrinsic overload that matches the provided argument types.
+    /// @param intrinsic the intrinsic being called
+    /// @param intrinsic_name the name of the intrinsic
+    /// @param args the argument types
+    /// @param templates initial template state. This may contain explicitly specified template
+    ///                  arguments. For example `vec3<f32>()` would have the first template-type
+    ///                  defined as `f32`.
+    /// @param on_no_match an error callback when no intrinsic overloads matched the provided
+    ///                    arguments.
+    /// @returns the matched intrinsic. If no intrinsic could be matched then IntrinsicPrototype
+    ///          will hold nullptrs for IntrinsicPrototype::overload and
+    ///          IntrinsicPrototype::return_type.
+    IntrinsicPrototype MatchIntrinsic(const IntrinsicInfo& intrinsic,
+                                      const char* intrinsic_name,
+                                      const std::vector<const sem::Type*>& args,
+                                      TemplateState templates,
+                                      OnNoMatch on_no_match) const;
+
+    /// Evaluates the overload for the provided argument types.
+    /// @param overload the overload being considered
+    /// @param args the argument types
+    /// @param templates initial template state. This may contain explicitly specified template
+    ///                  arguments. For example `vec3<f32>()` would have the first template-type
+    ///                  template as `f32`.
+    /// @returns the evaluated Candidate information.
+    Candidate ScoreOverload(const OverloadInfo* overload,
+                            const std::vector<const sem::Type*>& args,
+                            TemplateState templates) const;
+
+    /// Match constructs a new MatchState
+    /// @param templates the template state used for matcher evaluation
+    /// @param overload the overload being evaluated
+    /// @param matcher_indices pointer to a list of matcher indices
+    MatchState Match(TemplateState& templates,
+                     const OverloadInfo* overload,
                      MatcherIndex const* matcher_indices) const;
 
-    void PrintOverload(std::ostream& ss, const OverloadInfo& overload, const char* name) const;
+    // Prints the overload for emitting diagnostics
+    void PrintOverload(std::ostream& ss,
+                       const OverloadInfo* overload,
+                       const char* intrinsic_name) const;
+
+    // Prints the list of candidates for emitting diagnostics
+    void PrintCandidates(std::ostream& ss,
+                         const Candidates& candidates,
+                         const char* intrinsic_name) const;
+
+    /// Raises an ICE when multiple overload candidates match, as this should never happen.
+    void ErrMultipleOverloadsMatched(size_t num_matched,
+                                     const char* intrinsic_name,
+                                     const std::vector<const sem::Type*>& args,
+                                     TemplateState templates,
+                                     Candidates candidates) const;
 
     ProgramBuilder& builder;
     Matchers matchers;
     std::unordered_map<IntrinsicPrototype, sem::Builtin*, IntrinsicPrototype::Hasher> builtins;
+    std::unordered_map<IntrinsicPrototype, sem::TypeConstructor*, IntrinsicPrototype::Hasher>
+        constructors;
+    std::unordered_map<IntrinsicPrototype, sem::TypeConversion*, IntrinsicPrototype::Hasher>
+        converters;
 };
 
 /// @return a string representing a call to a builtin with the given argument
 /// types.
 std::string CallSignature(ProgramBuilder& builder,
                           const char* intrinsic_name,
-                          const std::vector<const sem::Type*>& args) {
+                          const std::vector<const sem::Type*>& args,
+                          const sem::Type* template_arg = nullptr) {
     std::stringstream ss;
-    ss << intrinsic_name << "(";
+    ss << intrinsic_name;
+    if (template_arg) {
+        ss << "<" << template_arg->FriendlyName(builder.Symbols()) << ">";
+    }
+    ss << "(";
     {
         bool first = true;
         for (auto* arg : args) {
@@ -883,12 +1019,12 @@
     return ss.str();
 }
 
-std::string OpenTypeMatcher::String(MatchState& state) const {
-    return state.overload.open_types[index_].name;
+std::string TemplateTypeMatcher::String(MatchState* state) const {
+    return state->overload->template_types[index_].name;
 }
 
-std::string OpenNumberMatcher::String(MatchState& state) const {
-    return state.overload.open_numbers[index_].name;
+std::string TemplateNumberMatcher::String(MatchState* state) const {
+    return state->overload->template_numbers[index_].name;
 }
 
 Impl::Impl(ProgramBuilder& b) : builder(b) {}
@@ -896,110 +1032,90 @@
 const sem::Builtin* Impl::Lookup(sem::BuiltinType builtin_type,
                                  const std::vector<const sem::Type*>& args,
                                  const Source& source) {
-    // The list of failed matches that had promise.
-    std::vector<Candidate> candidates;
-
-    uint32_t intrinsic_index = static_cast<uint32_t>(builtin_type);
     const char* intrinsic_name = sem::str(builtin_type);
-    auto& builtin = kBuiltins[intrinsic_index];
-    for (uint32_t o = 0; o < builtin.num_overloads; o++) {
-        int match_score = 1000;
-        auto& overload = builtin.overloads[o];
-        auto match = Match(intrinsic_name, intrinsic_index, overload, args, match_score);
-        if (match.return_type) {
-            // De-duplicate builtins that are identical.
-            return utils::GetOrCreate(builtins, match, [&] {
-                std::vector<sem::Parameter*> params;
-                params.reserve(match.parameters.size());
-                for (auto& p : match.parameters) {
-                    params.emplace_back(builder.create<sem::Parameter>(
-                        nullptr, static_cast<uint32_t>(params.size()), p.type,
-                        ast::StorageClass::kNone, ast::Access::kUndefined, p.usage));
-                }
-                return builder.create<sem::Builtin>(builtin_type, match.return_type,
-                                                    std::move(params), match.supported_stages,
-                                                    match.is_deprecated);
-            });
+
+    // Generates an error when no overloads match the provided arguments
+    auto on_no_match = [&](Candidates candidates) {
+        std::stringstream ss;
+        ss << "no matching call to " << CallSignature(builder, intrinsic_name, args) << std::endl;
+        if (!candidates.empty()) {
+            ss << std::endl
+               << candidates.size() << " candidate function" << (candidates.size() > 1 ? "s:" : ":")
+               << std::endl;
+            PrintCandidates(ss, candidates, intrinsic_name);
         }
-        if (match_score > 0) {
-            candidates.emplace_back(Candidate{&overload, match_score});
-        }
+        builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
+    };
+
+    // Resolve the intrinsic overload
+    auto match = MatchIntrinsic(kBuiltins[static_cast<size_t>(builtin_type)], intrinsic_name, args,
+                                TemplateState{}, on_no_match);
+    if (!match.overload) {
+        return {};
     }
 
-    // Sort the candidates with the most promising first
-    std::stable_sort(candidates.begin(), candidates.end(),
-                     [](const Candidate& a, const Candidate& b) { return a.score > b.score; });
-
-    // Generate an error message
-    std::stringstream ss;
-    ss << "no matching call to " << CallSignature(builder, intrinsic_name, args) << std::endl;
-    if (!candidates.empty()) {
-        ss << std::endl;
-        ss << candidates.size() << " candidate function" << (candidates.size() > 1 ? "s:" : ":")
-           << std::endl;
-        for (auto& candidate : candidates) {
-            ss << "  ";
-            PrintOverload(ss, *candidate.overload, intrinsic_name);
-            ss << std::endl;
+    // De-duplicate builtins that are identical.
+    return utils::GetOrCreate(builtins, match, [&] {
+        std::vector<sem::Parameter*> params;
+        params.reserve(match.parameters.size());
+        for (auto& p : match.parameters) {
+            params.emplace_back(builder.create<sem::Parameter>(
+                nullptr, static_cast<uint32_t>(params.size()), p.type, ast::StorageClass::kNone,
+                ast::Access::kUndefined, p.usage));
         }
-    }
-    builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
-    return nullptr;
+        sem::PipelineStageSet supported_stages;
+        if (match.overload->flags.Contains(OverloadFlag::kSupportsVertexPipeline)) {
+            supported_stages.Add(ast::PipelineStage::kVertex);
+        }
+        if (match.overload->flags.Contains(OverloadFlag::kSupportsFragmentPipeline)) {
+            supported_stages.Add(ast::PipelineStage::kFragment);
+        }
+        if (match.overload->flags.Contains(OverloadFlag::kSupportsComputePipeline)) {
+            supported_stages.Add(ast::PipelineStage::kCompute);
+        }
+        return builder.create<sem::Builtin>(
+            builtin_type, match.return_type, std::move(params), supported_stages,
+            match.overload->flags.Contains(OverloadFlag::kIsDeprecated));
+    });
 }
 
 IntrinsicTable::UnaryOperator Impl::Lookup(ast::UnaryOp op,
                                            const sem::Type* arg,
                                            const Source& source) {
-    // The list of failed matches that had promise.
-    std::vector<Candidate> candidates;
-
-    auto [intrinsic_index, intrinsic_name] = [&]() -> std::pair<uint32_t, const char*> {
+    auto [intrinsic_index, intrinsic_name] = [&]() -> std::pair<size_t, const char*> {
         switch (op) {
             case ast::UnaryOp::kComplement:
-                return {kOperatorComplement, "operator ~ "};
+                return {kUnaryOperatorComplement, "operator ~ "};
             case ast::UnaryOp::kNegation:
-                return {kOperatorMinus, "operator - "};
+                return {kUnaryOperatorMinus, "operator - "};
             case ast::UnaryOp::kNot:
-                return {kOperatorNot, "operator ! "};
+                return {kUnaryOperatorNot, "operator ! "};
             default:
                 return {0, "<unknown>"};
         }
     }();
 
-    auto& builtin = kOperators[intrinsic_index];
-    for (uint32_t o = 0; o < builtin.num_overloads; o++) {
-        int match_score = 1000;
-        auto& overload = builtin.overloads[o];
-        if (overload.num_parameters == 1) {
-            auto match = Match(intrinsic_name, intrinsic_index, overload, {arg}, match_score);
-            if (match.return_type) {
-                return UnaryOperator{match.return_type, match.parameters[0].type};
-            }
-            if (match_score > 0) {
-                candidates.emplace_back(Candidate{&overload, match_score});
-            }
+    // Generates an error when no overloads match the provided arguments
+    auto on_no_match = [&, name = intrinsic_name](Candidates candidates) {
+        std::stringstream ss;
+        ss << "no matching overload for " << CallSignature(builder, name, {arg}) << std::endl;
+        if (!candidates.empty()) {
+            ss << std::endl
+               << candidates.size() << " candidate operator" << (candidates.size() > 1 ? "s:" : ":")
+               << std::endl;
+            PrintCandidates(ss, candidates, name);
         }
+        builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
+    };
+
+    // Resolve the intrinsic overload
+    auto match = MatchIntrinsic(kUnaryOperators[intrinsic_index], intrinsic_name, {arg},
+                                TemplateState{}, on_no_match);
+    if (!match.overload) {
+        return {};
     }
 
-    // Sort the candidates with the most promising first
-    std::stable_sort(candidates.begin(), candidates.end(),
-                     [](const Candidate& a, const Candidate& b) { return a.score > b.score; });
-
-    // Generate an error message
-    std::stringstream ss;
-    ss << "no matching overload for " << CallSignature(builder, intrinsic_name, {arg}) << std::endl;
-    if (!candidates.empty()) {
-        ss << std::endl;
-        ss << candidates.size() << " candidate operator" << (candidates.size() > 1 ? "s:" : ":")
-           << std::endl;
-        for (auto& candidate : candidates) {
-            ss << "  ";
-            PrintOverload(ss, *candidate.overload, intrinsic_name);
-            ss << std::endl;
-        }
-    }
-    builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
-    return {};
+    return UnaryOperator{match.return_type, match.parameters[0].type};
 }
 
 IntrinsicTable::BinaryOperator Impl::Lookup(ast::BinaryOp op,
@@ -1007,200 +1123,305 @@
                                             const sem::Type* rhs,
                                             const Source& source,
                                             bool is_compound) {
-    // The list of failed matches that had promise.
-    std::vector<Candidate> candidates;
-
-    auto [intrinsic_index, intrinsic_name] = [&]() -> std::pair<uint32_t, const char*> {
+    auto [intrinsic_index, intrinsic_name] = [&]() -> std::pair<size_t, const char*> {
         switch (op) {
             case ast::BinaryOp::kAnd:
-                return {kOperatorAnd, is_compound ? "operator &= " : "operator & "};
+                return {kBinaryOperatorAnd, is_compound ? "operator &= " : "operator & "};
             case ast::BinaryOp::kOr:
-                return {kOperatorOr, is_compound ? "operator |= " : "operator | "};
+                return {kBinaryOperatorOr, is_compound ? "operator |= " : "operator | "};
             case ast::BinaryOp::kXor:
-                return {kOperatorXor, is_compound ? "operator ^= " : "operator ^ "};
+                return {kBinaryOperatorXor, is_compound ? "operator ^= " : "operator ^ "};
             case ast::BinaryOp::kLogicalAnd:
-                return {kOperatorLogicalAnd, "operator && "};
+                return {kBinaryOperatorLogicalAnd, "operator && "};
             case ast::BinaryOp::kLogicalOr:
-                return {kOperatorLogicalOr, "operator || "};
+                return {kBinaryOperatorLogicalOr, "operator || "};
             case ast::BinaryOp::kEqual:
-                return {kOperatorEqual, "operator == "};
+                return {kBinaryOperatorEqual, "operator == "};
             case ast::BinaryOp::kNotEqual:
-                return {kOperatorNotEqual, "operator != "};
+                return {kBinaryOperatorNotEqual, "operator != "};
             case ast::BinaryOp::kLessThan:
-                return {kOperatorLessThan, "operator < "};
+                return {kBinaryOperatorLessThan, "operator < "};
             case ast::BinaryOp::kGreaterThan:
-                return {kOperatorGreaterThan, "operator > "};
+                return {kBinaryOperatorGreaterThan, "operator > "};
             case ast::BinaryOp::kLessThanEqual:
-                return {kOperatorLessThanEqual, "operator <= "};
+                return {kBinaryOperatorLessThanEqual, "operator <= "};
             case ast::BinaryOp::kGreaterThanEqual:
-                return {kOperatorGreaterThanEqual, "operator >= "};
+                return {kBinaryOperatorGreaterThanEqual, "operator >= "};
             case ast::BinaryOp::kShiftLeft:
-                return {kOperatorShiftLeft, is_compound ? "operator <<= " : "operator << "};
+                return {kBinaryOperatorShiftLeft, is_compound ? "operator <<= " : "operator << "};
             case ast::BinaryOp::kShiftRight:
-                return {kOperatorShiftRight, is_compound ? "operator >>= " : "operator >> "};
+                return {kBinaryOperatorShiftRight, is_compound ? "operator >>= " : "operator >> "};
             case ast::BinaryOp::kAdd:
-                return {kOperatorPlus, is_compound ? "operator += " : "operator + "};
+                return {kBinaryOperatorPlus, is_compound ? "operator += " : "operator + "};
             case ast::BinaryOp::kSubtract:
-                return {kOperatorMinus, is_compound ? "operator -= " : "operator - "};
+                return {kBinaryOperatorMinus, is_compound ? "operator -= " : "operator - "};
             case ast::BinaryOp::kMultiply:
-                return {kOperatorStar, is_compound ? "operator *= " : "operator * "};
+                return {kBinaryOperatorStar, is_compound ? "operator *= " : "operator * "};
             case ast::BinaryOp::kDivide:
-                return {kOperatorDivide, is_compound ? "operator /= " : "operator / "};
+                return {kBinaryOperatorDivide, is_compound ? "operator /= " : "operator / "};
             case ast::BinaryOp::kModulo:
-                return {kOperatorModulo, is_compound ? "operator %= " : "operator % "};
+                return {kBinaryOperatorModulo, is_compound ? "operator %= " : "operator % "};
             default:
                 return {0, "<unknown>"};
         }
     }();
 
-    auto& builtin = kOperators[intrinsic_index];
-    for (uint32_t o = 0; o < builtin.num_overloads; o++) {
-        int match_score = 1000;
-        auto& overload = builtin.overloads[o];
-        if (overload.num_parameters == 2) {
-            auto match = Match(intrinsic_name, intrinsic_index, overload, {lhs, rhs}, match_score);
-            if (match.return_type) {
-                return BinaryOperator{match.return_type, match.parameters[0].type,
-                                      match.parameters[1].type};
-            }
-            if (match_score > 0) {
-                candidates.emplace_back(Candidate{&overload, match_score});
+    // Generates an error when no overloads match the provided arguments
+    auto on_no_match = [&, name = intrinsic_name](Candidates candidates) {
+        std::stringstream ss;
+        ss << "no matching overload for " << CallSignature(builder, name, {lhs, rhs}) << std::endl;
+        if (!candidates.empty()) {
+            ss << std::endl
+               << candidates.size() << " candidate operator" << (candidates.size() > 1 ? "s:" : ":")
+               << std::endl;
+            PrintCandidates(ss, candidates, name);
+        }
+        builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
+    };
+
+    // Resolve the intrinsic overload
+    auto match = MatchIntrinsic(kBinaryOperators[intrinsic_index], intrinsic_name, {lhs, rhs},
+                                TemplateState{}, on_no_match);
+    if (!match.overload) {
+        return {};
+    }
+
+    return BinaryOperator{match.return_type, match.parameters[0].type, match.parameters[1].type};
+}
+
+const sem::CallTarget* Impl::Lookup(CtorConvIntrinsic type,
+                                    const sem::Type* template_arg,
+                                    const std::vector<const sem::Type*>& args,
+                                    const Source& source) {
+    auto name = str(type);
+
+    // Generates an error when no overloads match the provided arguments
+    auto on_no_match = [&](Candidates candidates) {
+        std::stringstream ss;
+        ss << "no matching constructor for " << CallSignature(builder, name, args, template_arg)
+           << std::endl;
+        Candidates ctor, conv;
+        for (auto candidate : candidates) {
+            if (candidate.overload->flags.Contains(OverloadFlag::kIsConstructor)) {
+                ctor.emplace_back(candidate);
+            } else {
+                conv.emplace_back(candidate);
             }
         }
+        if (!ctor.empty()) {
+            ss << std::endl
+               << ctor.size() << " candidate constructor" << (ctor.size() > 1 ? "s:" : ":")
+               << std::endl;
+            PrintCandidates(ss, ctor, name);
+        }
+        if (!conv.empty()) {
+            ss << std::endl
+               << conv.size() << " candidate conversion" << (conv.size() > 1 ? "s:" : ":")
+               << std::endl;
+            PrintCandidates(ss, conv, name);
+        }
+        builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
+    };
+
+    // If a template type was provided, then close the 0'th type with this.
+    TemplateState templates;
+    if (template_arg) {
+        templates.Type(0, template_arg);
+    }
+
+    // Resolve the intrinsic overload
+    auto match = MatchIntrinsic(kConstructorsAndConverters[static_cast<size_t>(type)], name, args,
+                                templates, on_no_match);
+    if (!match.overload) {
+        return {};
+    }
+
+    // Was this overload a constructor or conversion?
+    if (match.overload->flags.Contains(OverloadFlag::kIsConstructor)) {
+        sem::ParameterList params;
+        params.reserve(match.parameters.size());
+        for (auto& p : match.parameters) {
+            params.emplace_back(builder.create<sem::Parameter>(
+                nullptr, static_cast<uint32_t>(params.size()), p.type, ast::StorageClass::kNone,
+                ast::Access::kUndefined, p.usage));
+        }
+        return utils::GetOrCreate(constructors, match, [&]() {
+            return builder.create<sem::TypeConstructor>(match.return_type, std::move(params));
+        });
+    }
+
+    // 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);
+        return builder.create<sem::TypeConversion>(match.return_type, param);
+    });
+}
+
+IntrinsicPrototype Impl::MatchIntrinsic(const IntrinsicInfo& intrinsic,
+                                        const char* intrinsic_name,
+                                        const std::vector<const sem::Type*>& args,
+                                        TemplateState templates,
+                                        OnNoMatch on_no_match) const {
+    size_t num_matched = 0;
+    Candidates candidates;
+    candidates.reserve(intrinsic.num_overloads);
+    for (size_t overload_idx = 0; overload_idx < static_cast<size_t>(intrinsic.num_overloads);
+         overload_idx++) {
+        auto candidate = ScoreOverload(&intrinsic.overloads[overload_idx], args, templates);
+        if (candidate.score == 0) {
+            num_matched++;
+        }
+        candidates.emplace_back(std::move(candidate));
     }
 
     // Sort the candidates with the most promising first
     std::stable_sort(candidates.begin(), candidates.end(),
-                     [](const Candidate& a, const Candidate& b) { return a.score > b.score; });
+                     [&](const Candidate& a, const Candidate& b) { return a.score < b.score; });
 
-    // Generate an error message
-    std::stringstream ss;
-    ss << "no matching overload for " << CallSignature(builder, intrinsic_name, {lhs, rhs})
-       << std::endl;
-    if (!candidates.empty()) {
-        ss << std::endl;
-        ss << candidates.size() << " candidate operator" << (candidates.size() > 1 ? "s:" : ":")
-           << std::endl;
-        for (auto& candidate : candidates) {
-            ss << "  ";
-            PrintOverload(ss, *candidate.overload, intrinsic_name);
-            ss << std::endl;
-        }
-    }
-    builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
-    return {};
-}
-
-const IntrinsicPrototype Impl::Match(const char* intrinsic_name,
-                                     uint32_t intrinsic_index,
-                                     const OverloadInfo& overload,
-                                     const std::vector<const sem::Type*>& args,
-                                     int& match_score) {
-    // Score wait for argument <-> parameter count matches / mismatches
-    constexpr int kScorePerParamArgMismatch = -1;
-    constexpr int kScorePerMatchedParam = 2;
-    constexpr int kScorePerMatchedOpenType = 1;
-    constexpr int kScorePerMatchedOpenNumber = 1;
-
-    auto num_parameters = overload.num_parameters;
-    auto num_arguments = static_cast<decltype(num_parameters)>(args.size());
-
-    bool overload_matched = true;
-
-    if (num_parameters != num_arguments) {
-        match_score += kScorePerParamArgMismatch * (std::max(num_parameters, num_arguments) -
-                                                    std::min(num_parameters, num_arguments));
-        overload_matched = false;
+    // How many candidates matched?
+    switch (num_matched) {
+        case 0:
+            on_no_match(std::move(candidates));
+            return {};
+        case 1:
+            break;
+        default:
+            // Note: Currently the intrinsic table does not contain any overloads which may result
+            // in ambiguities, so here we call ErrMultipleOverloadsMatched() which will produce and
+            // ICE. If we end up in the situation where this is unavoidable, we'll need to perform
+            // further overload resolution as described in
+            // https://www.w3.org/TR/WGSL/#overload-resolution-section.
+            ErrMultipleOverloadsMatched(num_matched, intrinsic_name, args, templates, candidates);
     }
 
-    ClosedState closed(builder);
-
-    std::vector<IntrinsicPrototype::Parameter> parameters;
-
-    auto num_params = std::min(num_parameters, num_arguments);
-    for (uint32_t p = 0; p < num_params; p++) {
-        auto& parameter = overload.parameters[p];
-        auto* indices = parameter.matcher_indices;
-        auto* type = Match(closed, overload, indices).Type(args[p]->UnwrapRef());
-        if (type) {
-            parameters.emplace_back(IntrinsicPrototype::Parameter{type, parameter.usage});
-            match_score += kScorePerMatchedParam;
-        } else {
-            overload_matched = false;
-        }
-    }
-
-    if (overload_matched) {
-        // Check all constrained open types matched
-        for (uint32_t ot = 0; ot < overload.num_open_types; ot++) {
-            auto& open_type = overload.open_types[ot];
-            if (open_type.matcher_index != kNoMatcher) {
-                auto* index = &open_type.matcher_index;
-                if (Match(closed, overload, index).Type(closed.Type(ot))) {
-                    match_score += kScorePerMatchedOpenType;
-                } else {
-                    overload_matched = false;
-                }
-            }
-        }
-    }
-
-    if (overload_matched) {
-        // Check all constrained open numbers matched
-        for (uint32_t on = 0; on < overload.num_open_numbers; on++) {
-            auto& open_number = overload.open_numbers[on];
-            if (open_number.matcher_index != kNoMatcher) {
-                auto* index = &open_number.matcher_index;
-                if (Match(closed, overload, index).Num(closed.Num(on)).IsValid()) {
-                    match_score += kScorePerMatchedOpenNumber;
-                } else {
-                    overload_matched = false;
-                }
-            }
-        }
-    }
-
-    if (!overload_matched) {
-        return {};
-    }
+    auto match = candidates[0];
 
     // Build the return type
     const sem::Type* return_type = nullptr;
-    if (auto* indices = overload.return_matcher_indices) {
+    if (auto* indices = match.overload->return_matcher_indices) {
         Any any;
-        return_type = Match(closed, overload, indices).Type(&any);
+        return_type = Match(match.templates, match.overload, indices).Type(&any);
         if (!return_type) {
-            std::stringstream ss;
-            PrintOverload(ss, overload, intrinsic_name);
-            TINT_ICE(Resolver, builder.Diagnostics())
-                << "MatchState.Match() returned null for " << ss.str();
+            TINT_ICE(Resolver, builder.Diagnostics()) << "MatchState.Match() returned null";
             return {};
         }
     } else {
         return_type = builder.create<sem::Void>();
     }
 
-    IntrinsicPrototype builtin;
-    builtin.index = intrinsic_index;
-    builtin.return_type = return_type;
-    builtin.parameters = std::move(parameters);
-    builtin.supported_stages = overload.supported_stages;
-    builtin.is_deprecated = overload.is_deprecated;
-    return builtin;
+    return IntrinsicPrototype{match.overload, return_type, std::move(match.parameters)};
 }
 
-MatchState Impl::Match(ClosedState& closed,
-                       const OverloadInfo& overload,
+Impl::Candidate Impl::ScoreOverload(const OverloadInfo* overload,
+                                    const std::vector<const sem::Type*>& args,
+                                    TemplateState templates) const {
+    // Penalty weights for overload mismatching.
+    // This scoring is used to order the suggested overloads in diagnostic on overload mismatch, and
+    // has no impact for a correct program.
+    // The overloads with the lowest score will be displayed first (top-most).
+    constexpr int kMismatchedParamCountPenalty = 3;
+    constexpr int kMismatchedParamTypePenalty = 2;
+    constexpr int kMismatchedTemplateTypePenalty = 1;
+    constexpr int kMismatchedTemplateNumberPenalty = 1;
+
+    size_t num_parameters = static_cast<size_t>(overload->num_parameters);
+    size_t num_arguments = static_cast<size_t>(args.size());
+
+    size_t score = 0;
+
+    if (num_parameters != num_arguments) {
+        score += kMismatchedParamCountPenalty * (std::max(num_parameters, num_arguments) -
+                                                 std::min(num_parameters, num_arguments));
+    }
+
+    // Invoke the matchers for each parameter <-> argument pair.
+    // If any arguments cannot be matched, then `score` will be increased.
+    // If the overload has any template types or numbers then these will be set based on the
+    // argument types. Template types may be refined by constraining with later argument types. For
+    // example calling `F<T>(T, T)` with the argument types (abstract-int, i32) will first set T to
+    // abstract-int when matching the first argument, and then constrained down to i32 when matching
+    // the second argument.
+    // Note that inferred template types are not tested against their matchers at this point.
+    auto num_params = std::min(num_parameters, num_arguments);
+    for (size_t p = 0; p < num_params; p++) {
+        auto& parameter = overload->parameters[p];
+        auto* indices = parameter.matcher_indices;
+        if (!Match(templates, overload, indices).Type(args[p]->UnwrapRef())) {
+            score += kMismatchedParamTypePenalty;
+        }
+    }
+
+    if (score == 0) {
+        // Check all constrained template types matched their constraint matchers.
+        // If the template type *does not* match any of the types in the constraint matcher, then
+        // `score` is incremented. If the template type *does* match a type, then the template type
+        // is replaced with the first matching type. The order of types in the template matcher is
+        // important here, which can be controlled with the [[precedence(N)]] decorations on the
+        // types in intrinsics.def.
+        for (size_t ot = 0; ot < overload->num_template_types; ot++) {
+            auto* matcher_index = &overload->template_types[ot].matcher_index;
+            if (*matcher_index != kNoMatcher) {
+                if (auto* template_type = templates.Type(ot)) {
+                    if (auto* ty = Match(templates, overload, matcher_index).Type(template_type)) {
+                        // Template type matched one of the types in the template type's matcher.
+                        // Replace the template type with this type.
+                        templates.SetType(ot, ty);
+                        continue;
+                    }
+                }
+                score += kMismatchedTemplateTypePenalty;
+            }
+        }
+    }
+
+    if (score == 0) {
+        // Check all constrained open numbers matched.
+        // Unlike template types, numbers are not constrained, so we're just checking that the
+        // inferred number matches the constraints on the overload. Increments `score` if the
+        // template numbers do not match their constraint matchers.
+        for (size_t on = 0; on < overload->num_template_numbers; on++) {
+            auto* matcher_index = &overload->template_numbers[on].matcher_index;
+            if (*matcher_index != kNoMatcher) {
+                auto template_num = templates.Num(on);
+                if (!template_num.IsValid() ||
+                    !Match(templates, overload, matcher_index).Num(template_num).IsValid()) {
+                    score += kMismatchedTemplateNumberPenalty;
+                }
+            }
+        }
+    }
+
+    // Now that all the template types have been finalized, we can construct the parameters.
+    std::vector<IntrinsicPrototype::Parameter> parameters;
+    if (score == 0) {
+        parameters.reserve(num_params);
+        for (size_t p = 0; p < num_params; p++) {
+            auto& parameter = overload->parameters[p];
+            auto* indices = parameter.matcher_indices;
+            auto* ty = Match(templates, overload, indices).Type(args[p]->UnwrapRef());
+            parameters.emplace_back(IntrinsicPrototype::Parameter{ty, parameter.usage});
+        }
+    }
+
+    return Candidate{overload, templates, parameters, score};
+}
+
+MatchState Impl::Match(TemplateState& templates,
+                       const OverloadInfo* overload,
                        MatcherIndex const* matcher_indices) const {
-    return MatchState(builder, closed, matchers, overload, matcher_indices);
+    return MatchState(builder, templates, matchers, overload, matcher_indices);
 }
 
-void Impl::PrintOverload(std::ostream& ss, const OverloadInfo& overload, const char* name) const {
-    ClosedState closed(builder);
+void Impl::PrintOverload(std::ostream& ss,
+                         const OverloadInfo* overload,
+                         const char* intrinsic_name) const {
+    TemplateState templates;
 
-    ss << name << "(";
-    for (uint32_t p = 0; p < overload.num_parameters; p++) {
-        auto& parameter = overload.parameters[p];
+    ss << intrinsic_name << "(";
+    for (size_t p = 0; p < overload->num_parameters; p++) {
+        auto& parameter = overload->parameters[p];
         if (p > 0) {
             ss << ", ";
         }
@@ -1208,13 +1429,13 @@
             ss << sem::str(parameter.usage) << ": ";
         }
         auto* indices = parameter.matcher_indices;
-        ss << Match(closed, overload, indices).TypeName();
+        ss << Match(templates, overload, indices).TypeName();
     }
     ss << ")";
-    if (overload.return_matcher_indices) {
+    if (overload->return_matcher_indices) {
         ss << " -> ";
-        auto* indices = overload.return_matcher_indices;
-        ss << Match(closed, overload, indices).TypeName();
+        auto* indices = overload->return_matcher_indices;
+        ss << Match(templates, overload, indices).TypeName();
     }
 
     bool first = true;
@@ -1222,26 +1443,36 @@
         ss << (first ? "  where: " : ", ");
         first = false;
     };
-    for (uint32_t i = 0; i < overload.num_open_types; i++) {
-        auto& open_type = overload.open_types[i];
-        if (open_type.matcher_index != kNoMatcher) {
+    for (size_t i = 0; i < overload->num_template_types; i++) {
+        auto& template_type = overload->template_types[i];
+        if (template_type.matcher_index != kNoMatcher) {
             separator();
-            ss << open_type.name;
-            auto* index = &open_type.matcher_index;
-            ss << " is " << Match(closed, overload, index).TypeName();
+            ss << template_type.name;
+            auto* index = &template_type.matcher_index;
+            ss << " is " << Match(templates, overload, index).TypeName();
         }
     }
-    for (uint32_t i = 0; i < overload.num_open_numbers; i++) {
-        auto& open_number = overload.open_numbers[i];
-        if (open_number.matcher_index != kNoMatcher) {
+    for (size_t i = 0; i < overload->num_template_numbers; i++) {
+        auto& template_number = overload->template_numbers[i];
+        if (template_number.matcher_index != kNoMatcher) {
             separator();
-            ss << open_number.name;
-            auto* index = &open_number.matcher_index;
-            ss << " is " << Match(closed, overload, index).NumName();
+            ss << template_number.name;
+            auto* index = &template_number.matcher_index;
+            ss << " is " << Match(templates, overload, index).NumName();
         }
     }
 }
 
+void Impl::PrintCandidates(std::ostream& ss,
+                           const Candidates& candidates,
+                           const char* intrinsic_name) const {
+    for (auto& candidate : candidates) {
+        ss << "  ";
+        PrintOverload(ss, candidate.overload, intrinsic_name);
+        ss << std::endl;
+    }
+}
+
 const sem::Type* MatchState::Type(const sem::Type* ty) {
     MatcherIndex matcher_index = *matcher_indices_++;
     auto* matcher = matchers.type[matcher_index];
@@ -1257,13 +1488,50 @@
 std::string MatchState::TypeName() {
     MatcherIndex matcher_index = *matcher_indices_++;
     auto* matcher = matchers.type[matcher_index];
-    return matcher->String(*this);
+    return matcher->String(this);
 }
 
 std::string MatchState::NumName() {
     MatcherIndex matcher_index = *matcher_indices_++;
     auto* matcher = matchers.number[matcher_index];
-    return matcher->String(*this);
+    return matcher->String(this);
+}
+
+void Impl::ErrMultipleOverloadsMatched(size_t num_matched,
+                                       const char* intrinsic_name,
+                                       const std::vector<const sem::Type*>& args,
+                                       TemplateState templates,
+                                       Candidates candidates) const {
+    std::stringstream ss;
+    ss << num_matched << " overloads matched " << intrinsic_name;
+    for (size_t i = 0; i < std::numeric_limits<size_t>::max(); i++) {
+        if (auto* ty = templates.Type(i)) {
+            ss << ((i == 0) ? "<" : ", ") << ty->FriendlyName(builder.Symbols());
+        } else {
+            if (i > 0) {
+                ss << ">";
+            }
+            break;
+        }
+    }
+    ss << "(";
+    bool first = true;
+    for (auto* arg : args) {
+        if (!first) {
+            ss << ", ";
+        }
+        first = false;
+        ss << arg->FriendlyName(builder.Symbols());
+    }
+    ss << "):\n";
+    for (auto& candidate : candidates) {
+        if (candidate.score == 0) {
+            ss << "  ";
+            PrintOverload(ss, candidate.overload, intrinsic_name);
+            ss << std::endl;
+        }
+    }
+    TINT_ICE(Resolver, builder.Diagnostics()) << ss.str();
 }
 
 }  // namespace
@@ -1274,7 +1542,7 @@
 
 IntrinsicTable::~IntrinsicTable() = default;
 
-/// TypeInfo for the Any type declared in the anonymous namespace above
-TINT_INSTANTIATE_TYPEINFO(Any);
+}  // namespace tint::resolver
 
-}  // namespace tint
+/// TypeInfo for the Any type declared in the anonymous namespace above
+TINT_INSTANTIATE_TYPEINFO(tint::resolver::Any);
diff --git a/src/tint/resolver/intrinsic_table.h b/src/tint/resolver/intrinsic_table.h
index e873202..5a8985f 100644
--- a/src/tint/resolver/intrinsic_table.h
+++ b/src/tint/resolver/intrinsic_table.h
@@ -19,6 +19,7 @@
 #include <string>
 #include <vector>
 
+#include "src/tint/resolver/ctor_conv_intrinsic.h"
 #include "src/tint/sem/builtin.h"
 
 // Forward declarations
@@ -26,7 +27,7 @@
 class ProgramBuilder;
 }  // namespace tint
 
-namespace tint {
+namespace tint::resolver {
 
 /// IntrinsicTable is a lookup table of all the WGSL builtin functions and intrinsic operators
 class IntrinsicTable {
@@ -89,8 +90,20 @@
                                   const sem::Type* rhs,
                                   const Source& source,
                                   bool is_compound) = 0;
+
+    /// Lookup looks for the type constructor or conversion overload for the given
+    /// CtorConvIntrinsic.
+    /// @param type the type being constructed or converted
+    /// @param template_arg the optional template argument
+    /// @param args the argument types passed to the constructor / conversion call
+    /// @param source the source of the call
+    /// @return a sem::TypeConstructor, sem::TypeConversion or nullptr if nothing matched
+    virtual const sem::CallTarget* Lookup(CtorConvIntrinsic type,
+                                          const sem::Type* template_arg,
+                                          const std::vector<const sem::Type*>& args,
+                                          const Source& source) = 0;
 };
 
-}  // namespace tint
+}  // namespace tint::resolver
 
 #endif  // SRC_TINT_RESOLVER_INTRINSIC_TABLE_H_
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index 9981bb8..c518e58 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -25,11 +25,11 @@
 // clang-format off
 
 /// TypeMatcher for 'type bool'
-/// @see src/tint/intrinsics.def:68:6
+/// @see src/tint/intrinsics.def:73:6
 class Bool : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -37,7 +37,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Bool::Match(MatchState& state, const sem::Type* ty) const {
@@ -47,16 +47,16 @@
   return build_bool(state);
 }
 
-std::string Bool::String(MatchState&) const {
+std::string Bool::String(MatchState*) const {
   return "bool";
 }
 
-/// TypeMatcher for 'type f32'
-/// @see src/tint/intrinsics.def:69:6
-class F32 : public TypeMatcher {
+/// TypeMatcher for 'type af'
+/// @see src/tint/intrinsics.def:74:51
+class Af : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -64,26 +64,57 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
-const sem::Type* F32::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_f32(ty)) {
+const sem::Type* Af::Match(MatchState& state, const sem::Type* ty) const {
+  if (!match_af(ty)) {
     return nullptr;
   }
-  return build_f32(state);
+  return build_af(state);
 }
 
-std::string F32::String(MatchState&) const {
-  return "f32";
+std::string Af::String(MatchState*) const {
+  std::stringstream ss;
+  ss << "abstract-float";
+  return ss.str();
+}
+
+/// TypeMatcher for 'type ai'
+/// @see src/tint/intrinsics.def:75:51
+class Ai : 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* Ai::Match(MatchState& state, const sem::Type* ty) const {
+  if (!match_ai(ty)) {
+    return nullptr;
+  }
+  return build_ai(state);
+}
+
+std::string Ai::String(MatchState*) const {
+  std::stringstream ss;
+  ss << "abstract-int";
+  return ss.str();
 }
 
 /// TypeMatcher for 'type i32'
-/// @see src/tint/intrinsics.def:70:6
+/// @see src/tint/intrinsics.def:76:24
 class I32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -91,7 +122,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* I32::Match(MatchState& state, const sem::Type* ty) const {
@@ -101,16 +132,16 @@
   return build_i32(state);
 }
 
-std::string I32::String(MatchState&) const {
+std::string I32::String(MatchState*) const {
   return "i32";
 }
 
 /// TypeMatcher for 'type u32'
-/// @see src/tint/intrinsics.def:71:6
+/// @see src/tint/intrinsics.def:77:24
 class U32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -118,7 +149,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* U32::Match(MatchState& state, const sem::Type* ty) const {
@@ -128,16 +159,16 @@
   return build_u32(state);
 }
 
-std::string U32::String(MatchState&) const {
+std::string U32::String(MatchState*) const {
   return "u32";
 }
 
-/// TypeMatcher for 'type vec2'
-/// @see src/tint/intrinsics.def:72:6
-class Vec2 : public TypeMatcher {
+/// TypeMatcher for 'type f32'
+/// @see src/tint/intrinsics.def:78:24
+class F32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -145,7 +176,34 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
+};
+
+const sem::Type* F32::Match(MatchState& state, const sem::Type* ty) const {
+  if (!match_f32(ty)) {
+    return nullptr;
+  }
+  return build_f32(state);
+}
+
+std::string F32::String(MatchState*) const {
+  return "f32";
+}
+
+/// TypeMatcher for 'type vec2'
+/// @see src/tint/intrinsics.def:79:6
+class Vec2 : 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* Vec2::Match(MatchState& state, const sem::Type* ty) const {
@@ -160,17 +218,17 @@
   return build_vec2(state, T);
 }
 
-std::string Vec2::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string Vec2::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "vec2<" + T + ">";
 }
 
 /// TypeMatcher for 'type vec3'
-/// @see src/tint/intrinsics.def:73:6
+/// @see src/tint/intrinsics.def:80:6
 class Vec3 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -178,7 +236,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Vec3::Match(MatchState& state, const sem::Type* ty) const {
@@ -193,17 +251,17 @@
   return build_vec3(state, T);
 }
 
-std::string Vec3::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string Vec3::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "vec3<" + T + ">";
 }
 
 /// TypeMatcher for 'type vec4'
-/// @see src/tint/intrinsics.def:74:6
+/// @see src/tint/intrinsics.def:81:6
 class Vec4 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -211,7 +269,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Vec4::Match(MatchState& state, const sem::Type* ty) const {
@@ -226,17 +284,17 @@
   return build_vec4(state, T);
 }
 
-std::string Vec4::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string Vec4::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "vec4<" + T + ">";
 }
 
-/// TypeMatcher for 'type vec'
-/// @see src/tint/intrinsics.def:75:37
-class Vec : public TypeMatcher {
+/// TypeMatcher for 'type mat2x2'
+/// @see src/tint/intrinsics.def:82:6
+class Mat2X2 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -244,7 +302,304 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
+};
+
+const sem::Type* Mat2X2::Match(MatchState& state, const sem::Type* ty) const {
+  const sem::Type* T = nullptr;
+  if (!match_mat2x2(ty, T)) {
+    return nullptr;
+  }
+  T = state.Type(T);
+  if (T == nullptr) {
+    return nullptr;
+  }
+  return build_mat2x2(state, T);
+}
+
+std::string Mat2X2::String(MatchState* state) const {
+  const std::string T = state->TypeName();
+  return "mat2x2<" + T + ">";
+}
+
+/// TypeMatcher for 'type mat2x3'
+/// @see src/tint/intrinsics.def:83:6
+class Mat2X3 : 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* Mat2X3::Match(MatchState& state, const sem::Type* ty) const {
+  const sem::Type* T = nullptr;
+  if (!match_mat2x3(ty, T)) {
+    return nullptr;
+  }
+  T = state.Type(T);
+  if (T == nullptr) {
+    return nullptr;
+  }
+  return build_mat2x3(state, T);
+}
+
+std::string Mat2X3::String(MatchState* state) const {
+  const std::string T = state->TypeName();
+  return "mat2x3<" + T + ">";
+}
+
+/// TypeMatcher for 'type mat2x4'
+/// @see src/tint/intrinsics.def:84:6
+class Mat2X4 : 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* Mat2X4::Match(MatchState& state, const sem::Type* ty) const {
+  const sem::Type* T = nullptr;
+  if (!match_mat2x4(ty, T)) {
+    return nullptr;
+  }
+  T = state.Type(T);
+  if (T == nullptr) {
+    return nullptr;
+  }
+  return build_mat2x4(state, T);
+}
+
+std::string Mat2X4::String(MatchState* state) const {
+  const std::string T = state->TypeName();
+  return "mat2x4<" + T + ">";
+}
+
+/// TypeMatcher for 'type mat3x2'
+/// @see src/tint/intrinsics.def:85:6
+class Mat3X2 : 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* Mat3X2::Match(MatchState& state, const sem::Type* ty) const {
+  const sem::Type* T = nullptr;
+  if (!match_mat3x2(ty, T)) {
+    return nullptr;
+  }
+  T = state.Type(T);
+  if (T == nullptr) {
+    return nullptr;
+  }
+  return build_mat3x2(state, T);
+}
+
+std::string Mat3X2::String(MatchState* state) const {
+  const std::string T = state->TypeName();
+  return "mat3x2<" + T + ">";
+}
+
+/// TypeMatcher for 'type mat3x3'
+/// @see src/tint/intrinsics.def:86:6
+class Mat3X3 : 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* Mat3X3::Match(MatchState& state, const sem::Type* ty) const {
+  const sem::Type* T = nullptr;
+  if (!match_mat3x3(ty, T)) {
+    return nullptr;
+  }
+  T = state.Type(T);
+  if (T == nullptr) {
+    return nullptr;
+  }
+  return build_mat3x3(state, T);
+}
+
+std::string Mat3X3::String(MatchState* state) const {
+  const std::string T = state->TypeName();
+  return "mat3x3<" + T + ">";
+}
+
+/// TypeMatcher for 'type mat3x4'
+/// @see src/tint/intrinsics.def:87:6
+class Mat3X4 : 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* Mat3X4::Match(MatchState& state, const sem::Type* ty) const {
+  const sem::Type* T = nullptr;
+  if (!match_mat3x4(ty, T)) {
+    return nullptr;
+  }
+  T = state.Type(T);
+  if (T == nullptr) {
+    return nullptr;
+  }
+  return build_mat3x4(state, T);
+}
+
+std::string Mat3X4::String(MatchState* state) const {
+  const std::string T = state->TypeName();
+  return "mat3x4<" + T + ">";
+}
+
+/// TypeMatcher for 'type mat4x2'
+/// @see src/tint/intrinsics.def:88:6
+class Mat4X2 : 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* Mat4X2::Match(MatchState& state, const sem::Type* ty) const {
+  const sem::Type* T = nullptr;
+  if (!match_mat4x2(ty, T)) {
+    return nullptr;
+  }
+  T = state.Type(T);
+  if (T == nullptr) {
+    return nullptr;
+  }
+  return build_mat4x2(state, T);
+}
+
+std::string Mat4X2::String(MatchState* state) const {
+  const std::string T = state->TypeName();
+  return "mat4x2<" + T + ">";
+}
+
+/// TypeMatcher for 'type mat4x3'
+/// @see src/tint/intrinsics.def:89:6
+class Mat4X3 : 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* Mat4X3::Match(MatchState& state, const sem::Type* ty) const {
+  const sem::Type* T = nullptr;
+  if (!match_mat4x3(ty, T)) {
+    return nullptr;
+  }
+  T = state.Type(T);
+  if (T == nullptr) {
+    return nullptr;
+  }
+  return build_mat4x3(state, T);
+}
+
+std::string Mat4X3::String(MatchState* state) const {
+  const std::string T = state->TypeName();
+  return "mat4x3<" + T + ">";
+}
+
+/// TypeMatcher for 'type mat4x4'
+/// @see src/tint/intrinsics.def:90:6
+class Mat4X4 : 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* Mat4X4::Match(MatchState& state, const sem::Type* ty) const {
+  const sem::Type* T = nullptr;
+  if (!match_mat4x4(ty, T)) {
+    return nullptr;
+  }
+  T = state.Type(T);
+  if (T == nullptr) {
+    return nullptr;
+  }
+  return build_mat4x4(state, T);
+}
+
+std::string Mat4X4::String(MatchState* state) const {
+  const std::string T = state->TypeName();
+  return "mat4x4<" + T + ">";
+}
+
+/// TypeMatcher for 'type vec'
+/// @see src/tint/intrinsics.def:91:37
+class Vec : 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* Vec::Match(MatchState& state, const sem::Type* ty) const {
@@ -264,20 +619,20 @@
   return build_vec(state, N, T);
 }
 
-std::string Vec::String(MatchState& state) const {
-  const std::string N = state.NumName();
-  const std::string T = state.TypeName();
+std::string Vec::String(MatchState* state) const {
+  const std::string N = state->NumName();
+  const std::string T = state->TypeName();
   std::stringstream ss;
   ss << "vec" << N << "<" << T << ">";
   return ss.str();
 }
 
 /// TypeMatcher for 'type mat'
-/// @see src/tint/intrinsics.def:76:37
+/// @see src/tint/intrinsics.def:92:37
 class Mat : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -285,7 +640,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Mat::Match(MatchState& state, const sem::Type* ty) const {
@@ -310,21 +665,21 @@
   return build_mat(state, N, M, T);
 }
 
-std::string Mat::String(MatchState& state) const {
-  const std::string N = state.NumName();
-  const std::string M = state.NumName();
-  const std::string T = state.TypeName();
+std::string Mat::String(MatchState* state) const {
+  const std::string N = state->NumName();
+  const std::string M = state->NumName();
+  const std::string T = state->TypeName();
   std::stringstream ss;
   ss << "mat" << N << "x" << M << "<" << T << ">";
   return ss.str();
 }
 
 /// TypeMatcher for 'type ptr'
-/// @see src/tint/intrinsics.def:77:6
+/// @see src/tint/intrinsics.def:93:6
 class Ptr : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -332,7 +687,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Ptr::Match(MatchState& state, const sem::Type* ty) const {
@@ -357,19 +712,19 @@
   return build_ptr(state, S, T, A);
 }
 
-std::string Ptr::String(MatchState& state) const {
-  const std::string S = state.NumName();
-  const std::string T = state.TypeName();
-  const std::string A = state.NumName();
+std::string Ptr::String(MatchState* state) const {
+  const std::string S = state->NumName();
+  const std::string T = state->TypeName();
+  const std::string A = state->NumName();
   return "ptr<" + S + ", " + T + ", " + A + ">";
 }
 
 /// TypeMatcher for 'type atomic'
-/// @see src/tint/intrinsics.def:78:6
+/// @see src/tint/intrinsics.def:94:6
 class Atomic : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -377,7 +732,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Atomic::Match(MatchState& state, const sem::Type* ty) const {
@@ -392,17 +747,17 @@
   return build_atomic(state, T);
 }
 
-std::string Atomic::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string Atomic::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "atomic<" + T + ">";
 }
 
 /// TypeMatcher for 'type array'
-/// @see src/tint/intrinsics.def:79:6
+/// @see src/tint/intrinsics.def:95:6
 class Array : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -410,7 +765,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Array::Match(MatchState& state, const sem::Type* ty) const {
@@ -425,17 +780,17 @@
   return build_array(state, T);
 }
 
-std::string Array::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string Array::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "array<" + T + ">";
 }
 
 /// TypeMatcher for 'type sampler'
-/// @see src/tint/intrinsics.def:80:6
+/// @see src/tint/intrinsics.def:96:6
 class Sampler : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -443,7 +798,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Sampler::Match(MatchState& state, const sem::Type* ty) const {
@@ -453,16 +808,16 @@
   return build_sampler(state);
 }
 
-std::string Sampler::String(MatchState&) const {
+std::string Sampler::String(MatchState*) const {
   return "sampler";
 }
 
 /// TypeMatcher for 'type sampler_comparison'
-/// @see src/tint/intrinsics.def:81:6
+/// @see src/tint/intrinsics.def:97:6
 class SamplerComparison : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -470,7 +825,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* SamplerComparison::Match(MatchState& state, const sem::Type* ty) const {
@@ -480,16 +835,16 @@
   return build_sampler_comparison(state);
 }
 
-std::string SamplerComparison::String(MatchState&) const {
+std::string SamplerComparison::String(MatchState*) const {
   return "sampler_comparison";
 }
 
 /// TypeMatcher for 'type texture_1d'
-/// @see src/tint/intrinsics.def:82:6
+/// @see src/tint/intrinsics.def:98:6
 class Texture1D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -497,7 +852,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Texture1D::Match(MatchState& state, const sem::Type* ty) const {
@@ -512,17 +867,17 @@
   return build_texture_1d(state, T);
 }
 
-std::string Texture1D::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string Texture1D::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "texture_1d<" + T + ">";
 }
 
 /// TypeMatcher for 'type texture_2d'
-/// @see src/tint/intrinsics.def:83:6
+/// @see src/tint/intrinsics.def:99:6
 class Texture2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -530,7 +885,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Texture2D::Match(MatchState& state, const sem::Type* ty) const {
@@ -545,17 +900,17 @@
   return build_texture_2d(state, T);
 }
 
-std::string Texture2D::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string Texture2D::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "texture_2d<" + T + ">";
 }
 
 /// TypeMatcher for 'type texture_2d_array'
-/// @see src/tint/intrinsics.def:84:6
+/// @see src/tint/intrinsics.def:100:6
 class Texture2DArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -563,7 +918,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Texture2DArray::Match(MatchState& state, const sem::Type* ty) const {
@@ -578,17 +933,17 @@
   return build_texture_2d_array(state, T);
 }
 
-std::string Texture2DArray::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string Texture2DArray::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "texture_2d_array<" + T + ">";
 }
 
 /// TypeMatcher for 'type texture_3d'
-/// @see src/tint/intrinsics.def:85:6
+/// @see src/tint/intrinsics.def:101:6
 class Texture3D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -596,7 +951,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Texture3D::Match(MatchState& state, const sem::Type* ty) const {
@@ -611,17 +966,17 @@
   return build_texture_3d(state, T);
 }
 
-std::string Texture3D::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string Texture3D::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "texture_3d<" + T + ">";
 }
 
 /// TypeMatcher for 'type texture_cube'
-/// @see src/tint/intrinsics.def:86:6
+/// @see src/tint/intrinsics.def:102:6
 class TextureCube : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -629,7 +984,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureCube::Match(MatchState& state, const sem::Type* ty) const {
@@ -644,17 +999,17 @@
   return build_texture_cube(state, T);
 }
 
-std::string TextureCube::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string TextureCube::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "texture_cube<" + T + ">";
 }
 
 /// TypeMatcher for 'type texture_cube_array'
-/// @see src/tint/intrinsics.def:87:6
+/// @see src/tint/intrinsics.def:103:6
 class TextureCubeArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -662,7 +1017,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureCubeArray::Match(MatchState& state, const sem::Type* ty) const {
@@ -677,17 +1032,17 @@
   return build_texture_cube_array(state, T);
 }
 
-std::string TextureCubeArray::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string TextureCubeArray::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "texture_cube_array<" + T + ">";
 }
 
 /// TypeMatcher for 'type texture_multisampled_2d'
-/// @see src/tint/intrinsics.def:88:6
+/// @see src/tint/intrinsics.def:104:6
 class TextureMultisampled2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -695,7 +1050,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureMultisampled2D::Match(MatchState& state, const sem::Type* ty) const {
@@ -710,17 +1065,17 @@
   return build_texture_multisampled_2d(state, T);
 }
 
-std::string TextureMultisampled2D::String(MatchState& state) const {
-  const std::string T = state.TypeName();
+std::string TextureMultisampled2D::String(MatchState* state) const {
+  const std::string T = state->TypeName();
   return "texture_multisampled_2d<" + T + ">";
 }
 
 /// TypeMatcher for 'type texture_depth_2d'
-/// @see src/tint/intrinsics.def:89:6
+/// @see src/tint/intrinsics.def:105:6
 class TextureDepth2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -728,7 +1083,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureDepth2D::Match(MatchState& state, const sem::Type* ty) const {
@@ -738,16 +1093,16 @@
   return build_texture_depth_2d(state);
 }
 
-std::string TextureDepth2D::String(MatchState&) const {
+std::string TextureDepth2D::String(MatchState*) const {
   return "texture_depth_2d";
 }
 
 /// TypeMatcher for 'type texture_depth_2d_array'
-/// @see src/tint/intrinsics.def:90:6
+/// @see src/tint/intrinsics.def:106:6
 class TextureDepth2DArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -755,7 +1110,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureDepth2DArray::Match(MatchState& state, const sem::Type* ty) const {
@@ -765,16 +1120,16 @@
   return build_texture_depth_2d_array(state);
 }
 
-std::string TextureDepth2DArray::String(MatchState&) const {
+std::string TextureDepth2DArray::String(MatchState*) const {
   return "texture_depth_2d_array";
 }
 
 /// TypeMatcher for 'type texture_depth_cube'
-/// @see src/tint/intrinsics.def:91:6
+/// @see src/tint/intrinsics.def:107:6
 class TextureDepthCube : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -782,7 +1137,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureDepthCube::Match(MatchState& state, const sem::Type* ty) const {
@@ -792,16 +1147,16 @@
   return build_texture_depth_cube(state);
 }
 
-std::string TextureDepthCube::String(MatchState&) const {
+std::string TextureDepthCube::String(MatchState*) const {
   return "texture_depth_cube";
 }
 
 /// TypeMatcher for 'type texture_depth_cube_array'
-/// @see src/tint/intrinsics.def:92:6
+/// @see src/tint/intrinsics.def:108:6
 class TextureDepthCubeArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -809,7 +1164,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureDepthCubeArray::Match(MatchState& state, const sem::Type* ty) const {
@@ -819,16 +1174,16 @@
   return build_texture_depth_cube_array(state);
 }
 
-std::string TextureDepthCubeArray::String(MatchState&) const {
+std::string TextureDepthCubeArray::String(MatchState*) const {
   return "texture_depth_cube_array";
 }
 
 /// TypeMatcher for 'type texture_depth_multisampled_2d'
-/// @see src/tint/intrinsics.def:93:6
+/// @see src/tint/intrinsics.def:109:6
 class TextureDepthMultisampled2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -836,7 +1191,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureDepthMultisampled2D::Match(MatchState& state, const sem::Type* ty) const {
@@ -846,16 +1201,16 @@
   return build_texture_depth_multisampled_2d(state);
 }
 
-std::string TextureDepthMultisampled2D::String(MatchState&) const {
+std::string TextureDepthMultisampled2D::String(MatchState*) const {
   return "texture_depth_multisampled_2d";
 }
 
 /// TypeMatcher for 'type texture_storage_1d'
-/// @see src/tint/intrinsics.def:94:6
+/// @see src/tint/intrinsics.def:110:6
 class TextureStorage1D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -863,7 +1218,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureStorage1D::Match(MatchState& state, const sem::Type* ty) const {
@@ -883,18 +1238,18 @@
   return build_texture_storage_1d(state, F, A);
 }
 
-std::string TextureStorage1D::String(MatchState& state) const {
-  const std::string F = state.NumName();
-  const std::string A = state.NumName();
+std::string TextureStorage1D::String(MatchState* state) const {
+  const std::string F = state->NumName();
+  const std::string A = state->NumName();
   return "texture_storage_1d<" + F + ", " + A + ">";
 }
 
 /// TypeMatcher for 'type texture_storage_2d'
-/// @see src/tint/intrinsics.def:95:6
+/// @see src/tint/intrinsics.def:111:6
 class TextureStorage2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -902,7 +1257,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureStorage2D::Match(MatchState& state, const sem::Type* ty) const {
@@ -922,18 +1277,18 @@
   return build_texture_storage_2d(state, F, A);
 }
 
-std::string TextureStorage2D::String(MatchState& state) const {
-  const std::string F = state.NumName();
-  const std::string A = state.NumName();
+std::string TextureStorage2D::String(MatchState* state) const {
+  const std::string F = state->NumName();
+  const std::string A = state->NumName();
   return "texture_storage_2d<" + F + ", " + A + ">";
 }
 
 /// TypeMatcher for 'type texture_storage_2d_array'
-/// @see src/tint/intrinsics.def:96:6
+/// @see src/tint/intrinsics.def:112:6
 class TextureStorage2DArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -941,7 +1296,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureStorage2DArray::Match(MatchState& state, const sem::Type* ty) const {
@@ -961,18 +1316,18 @@
   return build_texture_storage_2d_array(state, F, A);
 }
 
-std::string TextureStorage2DArray::String(MatchState& state) const {
-  const std::string F = state.NumName();
-  const std::string A = state.NumName();
+std::string TextureStorage2DArray::String(MatchState* state) const {
+  const std::string F = state->NumName();
+  const std::string A = state->NumName();
   return "texture_storage_2d_array<" + F + ", " + A + ">";
 }
 
 /// TypeMatcher for 'type texture_storage_3d'
-/// @see src/tint/intrinsics.def:97:6
+/// @see src/tint/intrinsics.def:113:6
 class TextureStorage3D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -980,7 +1335,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureStorage3D::Match(MatchState& state, const sem::Type* ty) const {
@@ -1000,18 +1355,18 @@
   return build_texture_storage_3d(state, F, A);
 }
 
-std::string TextureStorage3D::String(MatchState& state) const {
-  const std::string F = state.NumName();
-  const std::string A = state.NumName();
+std::string TextureStorage3D::String(MatchState* state) const {
+  const std::string F = state->NumName();
+  const std::string A = state->NumName();
   return "texture_storage_3d<" + F + ", " + A + ">";
 }
 
 /// TypeMatcher for 'type texture_external'
-/// @see src/tint/intrinsics.def:98:6
+/// @see src/tint/intrinsics.def:114:6
 class TextureExternal : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -1019,7 +1374,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* TextureExternal::Match(MatchState& state, const sem::Type* ty) const {
@@ -1029,16 +1384,16 @@
   return build_texture_external(state);
 }
 
-std::string TextureExternal::String(MatchState&) const {
+std::string TextureExternal::String(MatchState*) const {
   return "texture_external";
 }
 
 /// TypeMatcher for 'type __modf_result'
-/// @see src/tint/intrinsics.def:100:6
+/// @see src/tint/intrinsics.def:116:6
 class ModfResult : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -1046,7 +1401,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* ModfResult::Match(MatchState& state, const sem::Type* ty) const {
@@ -1056,16 +1411,16 @@
   return build_modf_result(state);
 }
 
-std::string ModfResult::String(MatchState&) const {
+std::string ModfResult::String(MatchState*) const {
   return "__modf_result";
 }
 
 /// TypeMatcher for 'type __modf_result_vec'
-/// @see src/tint/intrinsics.def:101:42
+/// @see src/tint/intrinsics.def:117:42
 class ModfResultVec : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -1073,7 +1428,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* ModfResultVec::Match(MatchState& state, const sem::Type* ty) const {
@@ -1088,19 +1443,19 @@
   return build_modf_result_vec(state, N);
 }
 
-std::string ModfResultVec::String(MatchState& state) const {
-  const std::string N = state.NumName();
+std::string ModfResultVec::String(MatchState* state) const {
+  const std::string N = state->NumName();
   std::stringstream ss;
   ss << "__modf_result_vec" << N;
   return ss.str();
 }
 
 /// TypeMatcher for 'type __frexp_result'
-/// @see src/tint/intrinsics.def:102:6
+/// @see src/tint/intrinsics.def:118:6
 class FrexpResult : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -1108,7 +1463,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* FrexpResult::Match(MatchState& state, const sem::Type* ty) const {
@@ -1118,16 +1473,16 @@
   return build_frexp_result(state);
 }
 
-std::string FrexpResult::String(MatchState&) const {
+std::string FrexpResult::String(MatchState*) const {
   return "__frexp_result";
 }
 
 /// TypeMatcher for 'type __frexp_result_vec'
-/// @see src/tint/intrinsics.def:103:43
+/// @see src/tint/intrinsics.def:119:43
 class FrexpResultVec : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -1135,7 +1490,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* FrexpResultVec::Match(MatchState& state, const sem::Type* ty) const {
@@ -1150,20 +1505,20 @@
   return build_frexp_result_vec(state, N);
 }
 
-std::string FrexpResultVec::String(MatchState& state) const {
-  const std::string N = state.NumName();
+std::string FrexpResultVec::String(MatchState* state) const {
+  const std::string N = state->NumName();
   std::stringstream ss;
   ss << "__frexp_result_vec" << N;
   return ss.str();
 }
 
 /// TypeMatcher for 'match fiu32'
-/// @see src/tint/intrinsics.def:111:7
+/// @see src/tint/intrinsics.def:127:7
 class Fiu32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
   /// expected, canonicalized type on success.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -1171,33 +1526,37 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Fiu32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_f32(ty)) {
-    return build_f32(state);
-  }
   if (match_i32(ty)) {
     return build_i32(state);
   }
   if (match_u32(ty)) {
     return build_u32(state);
   }
+  if (match_f32(ty)) {
+    return build_f32(state);
+  }
   return nullptr;
 }
 
-std::string Fiu32::String(MatchState&) const {
-  return "f32, i32 or u32";
+std::string Fiu32::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) << " or " << U32().String(nullptr);
+  return ss.str();
 }
 
 /// TypeMatcher for 'match fi32'
-/// @see src/tint/intrinsics.def:112:7
+/// @see src/tint/intrinsics.def:128:7
 class Fi32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
   /// expected, canonicalized type on success.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -1205,30 +1564,34 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Fi32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_f32(ty)) {
-    return build_f32(state);
-  }
   if (match_i32(ty)) {
     return build_i32(state);
   }
+  if (match_f32(ty)) {
+    return build_f32(state);
+  }
   return nullptr;
 }
 
-std::string Fi32::String(MatchState&) const {
-  return "f32 or i32";
+std::string Fi32::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 " << I32().String(nullptr);
+  return ss.str();
 }
 
 /// TypeMatcher for 'match iu32'
-/// @see src/tint/intrinsics.def:113:7
+/// @see src/tint/intrinsics.def:129:7
 class Iu32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
   /// expected, canonicalized type on success.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -1236,7 +1599,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Iu32::Match(MatchState& state, const sem::Type* ty) const {
@@ -1249,17 +1612,21 @@
   return nullptr;
 }
 
-std::string Iu32::String(MatchState&) const {
-  return "i32 or u32";
+std::string Iu32::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 << I32().String(nullptr) << " or " << U32().String(nullptr);
+  return ss.str();
 }
 
 /// TypeMatcher for 'match scalar'
-/// @see src/tint/intrinsics.def:114:7
+/// @see src/tint/intrinsics.def:130:7
 class Scalar : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
   /// expected, canonicalized type on success.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -1267,13 +1634,133 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* Scalar::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 Scalar::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 abstract_or_scalar'
+/// @see src/tint/intrinsics.def:131:7
+class AbstractOrScalar : 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* AbstractOrScalar::Match(MatchState& state, const sem::Type* ty) const {
+  if (match_af(ty)) {
+    return build_af(state);
+  }
+  if (match_ai(ty)) {
+    return build_ai(state);
+  }
+  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 AbstractOrScalar::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 << Ai().String(nullptr) << ", " << Af().String(nullptr) << ", " << F32().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:132:7
+class AfF32 : 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* AfF32::Match(MatchState& state, const sem::Type* ty) const {
+  if (match_af(ty)) {
+    return build_af(state);
+  }
+  if (match_f32(ty)) {
+    return build_f32(state);
+  }
+  return nullptr;
+}
+
+std::string AfF32::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) << " or " << F32().String(nullptr);
+  return ss.str();
+}
+
+/// TypeMatcher for 'match scalar_no_f32'
+/// @see src/tint/intrinsics.def:133:7
+class ScalarNoF32 : 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* ScalarNoF32::Match(MatchState& state, const sem::Type* ty) const {
   if (match_i32(ty)) {
     return build_i32(state);
   }
@@ -1286,23 +1773,141 @@
   return nullptr;
 }
 
-std::string Scalar::String(MatchState&) const {
-  return "f32, i32, u32 or bool";
+std::string ScalarNoF32::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 << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+  return ss.str();
+}
+
+/// TypeMatcher for 'match scalar_no_i32'
+/// @see src/tint/intrinsics.def:134:7
+class ScalarNoI32 : 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* ScalarNoI32::Match(MatchState& state, const sem::Type* ty) const {
+  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 ScalarNoI32::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) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+  return ss.str();
+}
+
+/// TypeMatcher for 'match scalar_no_u32'
+/// @see src/tint/intrinsics.def:135:7
+class ScalarNoU32 : 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* ScalarNoU32::Match(MatchState& state, const sem::Type* ty) const {
+  if (match_i32(ty)) {
+    return build_i32(state);
+  }
+  if (match_f32(ty)) {
+    return build_f32(state);
+  }
+  if (match_bool(ty)) {
+    return build_bool(state);
+  }
+  return nullptr;
+}
+
+std::string ScalarNoU32::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) << " or " << Bool().String(nullptr);
+  return ss.str();
+}
+
+/// TypeMatcher for 'match scalar_no_bool'
+/// @see src/tint/intrinsics.def:136:7
+class ScalarNoBool : 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* ScalarNoBool::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);
+  }
+  return nullptr;
+}
+
+std::string ScalarNoBool::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) << " or " << U32().String(nullptr);
+  return ss.str();
 }
 
 /// EnumMatcher for 'match f32_texel_format'
-/// @see src/tint/intrinsics.def:125:7
+/// @see src/tint/intrinsics.def:147:7
 class F32TexelFormat : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 Number F32TexelFormat::Match(MatchState&, Number number) const {
@@ -1319,23 +1924,23 @@
   }
 }
 
-std::string F32TexelFormat::String(MatchState&) const {
+std::string F32TexelFormat::String(MatchState*) const {
   return "rgba8unorm, rgba8snorm, rgba16float, r32float, rg32float or rgba32float";
 }
 
 /// EnumMatcher for 'match i32_texel_format'
-/// @see src/tint/intrinsics.def:127:7
+/// @see src/tint/intrinsics.def:149:7
 class I32TexelFormat : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 Number I32TexelFormat::Match(MatchState&, Number number) const {
@@ -1351,23 +1956,23 @@
   }
 }
 
-std::string I32TexelFormat::String(MatchState&) const {
+std::string I32TexelFormat::String(MatchState*) const {
   return "rgba8sint, rgba16sint, r32sint, rg32sint or rgba32sint";
 }
 
 /// EnumMatcher for 'match u32_texel_format'
-/// @see src/tint/intrinsics.def:129:7
+/// @see src/tint/intrinsics.def:151:7
 class U32TexelFormat : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 Number U32TexelFormat::Match(MatchState&, Number number) const {
@@ -1383,23 +1988,23 @@
   }
 }
 
-std::string U32TexelFormat::String(MatchState&) const {
+std::string U32TexelFormat::String(MatchState*) const {
   return "rgba8uint, rgba16uint, r32uint, rg32uint or rgba32uint";
 }
 
 /// EnumMatcher for 'match write_only'
-/// @see src/tint/intrinsics.def:132:7
+/// @see src/tint/intrinsics.def:154:7
 class WriteOnly : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 Number WriteOnly::Match(MatchState&, Number number) const {
@@ -1409,23 +2014,23 @@
   return Number::invalid;
 }
 
-std::string WriteOnly::String(MatchState&) const {
+std::string WriteOnly::String(MatchState*) const {
   return "write";
 }
 
 /// EnumMatcher for 'match function_private_workgroup'
-/// @see src/tint/intrinsics.def:134:7
+/// @see src/tint/intrinsics.def:156:7
 class FunctionPrivateWorkgroup : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 Number FunctionPrivateWorkgroup::Match(MatchState&, Number number) const {
@@ -1439,23 +2044,23 @@
   }
 }
 
-std::string FunctionPrivateWorkgroup::String(MatchState&) const {
+std::string FunctionPrivateWorkgroup::String(MatchState*) const {
   return "function, private or workgroup";
 }
 
 /// EnumMatcher for 'match workgroup_or_storage'
-/// @see src/tint/intrinsics.def:135:7
+/// @see src/tint/intrinsics.def:157:7
 class WorkgroupOrStorage : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 Number WorkgroupOrStorage::Match(MatchState&, Number number) const {
@@ -1468,7 +2073,7 @@
   }
 }
 
-std::string WorkgroupOrStorage::String(MatchState&) const {
+std::string WorkgroupOrStorage::String(MatchState*) const {
   return "workgroup or storage";
 }
 
@@ -1476,14 +2081,14 @@
 class Storage : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 Number Storage::Match(MatchState&, Number number) const {
@@ -1493,7 +2098,7 @@
   return Number::invalid;
 }
 
-std::string Storage::String(MatchState&) const {
+std::string Storage::String(MatchState*) const {
   return "storage";
 }
 
@@ -1501,14 +2106,14 @@
 class Write : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 Number Write::Match(MatchState&, Number number) const {
@@ -1518,7 +2123,7 @@
   return Number::invalid;
 }
 
-std::string Write::String(MatchState&) const {
+std::string Write::String(MatchState*) const {
   return "write";
 }
 
@@ -1526,14 +2131,14 @@
 class ReadWrite : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 Number ReadWrite::Match(MatchState&, Number number) const {
@@ -1543,24 +2148,36 @@
   return Number::invalid;
 }
 
-std::string ReadWrite::String(MatchState&) const {
+std::string ReadWrite::String(MatchState*) const {
   return "read_write";
 }
 
 /// Matchers holds type and number matchers
 class Matchers {
  private:
-  OpenTypeMatcher open_type_0_{0};
-  OpenNumberMatcher open_number_0_{0};
-  OpenNumberMatcher open_number_1_{1};
-  OpenNumberMatcher open_number_2_{2};
+  TemplateTypeMatcher template_type_0_{0};
+  TemplateTypeMatcher template_type_1_{1};
+  TemplateNumberMatcher template_number_0_{0};
+  TemplateNumberMatcher template_number_1_{1};
+  TemplateNumberMatcher template_number_2_{2};
   Bool Bool_;
-  F32 F32_;
+  Af Af_;
+  Ai Ai_;
   I32 I32_;
   U32 U32_;
+  F32 F32_;
   Vec2 Vec2_;
   Vec3 Vec3_;
   Vec4 Vec4_;
+  Mat2X2 Mat2X2_;
+  Mat2X3 Mat2X3_;
+  Mat2X4 Mat2X4_;
+  Mat3X2 Mat3X2_;
+  Mat3X3 Mat3X3_;
+  Mat3X4 Mat3X4_;
+  Mat4X2 Mat4X2_;
+  Mat4X3 Mat4X3_;
+  Mat4X4 Mat4X4_;
   Vec Vec_;
   Mat Mat_;
   Ptr Ptr_;
@@ -1593,6 +2210,12 @@
   Fi32 Fi32_;
   Iu32 Iu32_;
   Scalar Scalar_;
+  AbstractOrScalar AbstractOrScalar_;
+  AfF32 AfF32_;
+  ScalarNoF32 ScalarNoF32_;
+  ScalarNoI32 ScalarNoI32_;
+  ScalarNoU32 ScalarNoU32_;
+  ScalarNoBool ScalarNoBool_;
   F32TexelFormat F32TexelFormat_;
   I32TexelFormat I32TexelFormat_;
   U32TexelFormat U32TexelFormat_;
@@ -1609,55 +2232,73 @@
   /// Destructor
   ~Matchers();
 
-  /// The open-types, types, and type matchers
-  TypeMatcher const* const type[40] = {
-    /* [0] */ &open_type_0_,
-    /* [1] */ &Bool_,
-    /* [2] */ &F32_,
-    /* [3] */ &I32_,
-    /* [4] */ &U32_,
-    /* [5] */ &Vec2_,
-    /* [6] */ &Vec3_,
-    /* [7] */ &Vec4_,
-    /* [8] */ &Vec_,
-    /* [9] */ &Mat_,
-    /* [10] */ &Ptr_,
-    /* [11] */ &Atomic_,
-    /* [12] */ &Array_,
-    /* [13] */ &Sampler_,
-    /* [14] */ &SamplerComparison_,
-    /* [15] */ &Texture1D_,
-    /* [16] */ &Texture2D_,
-    /* [17] */ &Texture2DArray_,
-    /* [18] */ &Texture3D_,
-    /* [19] */ &TextureCube_,
-    /* [20] */ &TextureCubeArray_,
-    /* [21] */ &TextureMultisampled2D_,
-    /* [22] */ &TextureDepth2D_,
-    /* [23] */ &TextureDepth2DArray_,
-    /* [24] */ &TextureDepthCube_,
-    /* [25] */ &TextureDepthCubeArray_,
-    /* [26] */ &TextureDepthMultisampled2D_,
-    /* [27] */ &TextureStorage1D_,
-    /* [28] */ &TextureStorage2D_,
-    /* [29] */ &TextureStorage2DArray_,
-    /* [30] */ &TextureStorage3D_,
-    /* [31] */ &TextureExternal_,
-    /* [32] */ &ModfResult_,
-    /* [33] */ &ModfResultVec_,
-    /* [34] */ &FrexpResult_,
-    /* [35] */ &FrexpResultVec_,
-    /* [36] */ &Fiu32_,
-    /* [37] */ &Fi32_,
-    /* [38] */ &Iu32_,
-    /* [39] */ &Scalar_,
+  /// The template types, types, and type matchers
+  TypeMatcher const* const type[58] = {
+    /* [0] */ &template_type_0_,
+    /* [1] */ &template_type_1_,
+    /* [2] */ &Bool_,
+    /* [3] */ &Af_,
+    /* [4] */ &Ai_,
+    /* [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] */ &Fiu32_,
+    /* [49] */ &Fi32_,
+    /* [50] */ &Iu32_,
+    /* [51] */ &Scalar_,
+    /* [52] */ &AbstractOrScalar_,
+    /* [53] */ &AfF32_,
+    /* [54] */ &ScalarNoF32_,
+    /* [55] */ &ScalarNoI32_,
+    /* [56] */ &ScalarNoU32_,
+    /* [57] */ &ScalarNoBool_,
   };
 
-  /// The open-numbers, and number matchers
+  /// The template numbers, and number matchers
   NumberMatcher const* const number[12] = {
-    /* [0] */ &open_number_0_,
-    /* [1] */ &open_number_1_,
-    /* [2] */ &open_number_2_,
+    /* [0] */ &template_number_0_,
+    /* [1] */ &template_number_1_,
+    /* [2] */ &template_number_2_,
     /* [3] */ &F32TexelFormat_,
     /* [4] */ &I32TexelFormat_,
     /* [5] */ &U32TexelFormat_,
@@ -1674,159 +2315,207 @@
 Matchers::~Matchers() = default;
 
 constexpr MatcherIndex kMatcherIndices[] = {
-  /* [0] */ 30,
-  /* [1] */ 4,
-  /* [2] */ 10,
+  /* [0] */ 22,
+  /* [1] */ 0,
+  /* [2] */ 23,
   /* [3] */ 0,
   /* [4] */ 11,
-  /* [5] */ 0,
-  /* [6] */ 11,
-  /* [7] */ 10,
-  /* [8] */ 9,
-  /* [9] */ 12,
+  /* [5] */ 7,
+  /* [6] */ 22,
+  /* [7] */ 9,
+  /* [8] */ 24,
+  /* [9] */ 0,
   /* [10] */ 0,
-  /* [11] */ 0,
-  /* [12] */ 9,
-  /* [13] */ 0,
-  /* [14] */ 0,
-  /* [15] */ 2,
-  /* [16] */ 9,
+  /* [11] */ 21,
+  /* [12] */ 0,
+  /* [13] */ 1,
+  /* [14] */ 7,
+  /* [15] */ 21,
+  /* [16] */ 0,
   /* [17] */ 0,
-  /* [18] */ 1,
-  /* [19] */ 2,
-  /* [20] */ 9,
-  /* [21] */ 1,
-  /* [22] */ 0,
-  /* [23] */ 2,
-  /* [24] */ 9,
+  /* [18] */ 7,
+  /* [19] */ 21,
+  /* [20] */ 0,
+  /* [21] */ 2,
+  /* [22] */ 7,
+  /* [23] */ 21,
+  /* [24] */ 1,
   /* [25] */ 0,
-  /* [26] */ 2,
-  /* [27] */ 2,
-  /* [28] */ 9,
-  /* [29] */ 1,
-  /* [30] */ 2,
-  /* [31] */ 2,
-  /* [32] */ 8,
-  /* [33] */ 0,
-  /* [34] */ 2,
-  /* [35] */ 8,
-  /* [36] */ 0,
-  /* [37] */ 1,
-  /* [38] */ 8,
+  /* [26] */ 7,
+  /* [27] */ 21,
+  /* [28] */ 1,
+  /* [29] */ 2,
+  /* [30] */ 7,
+  /* [31] */ 20,
+  /* [32] */ 0,
+  /* [33] */ 7,
+  /* [34] */ 41,
+  /* [35] */ 0,
+  /* [36] */ 1,
+  /* [37] */ 20,
+  /* [38] */ 0,
   /* [39] */ 0,
-  /* [40] */ 0,
-  /* [41] */ 29,
-  /* [42] */ 0,
-  /* [43] */ 1,
-  /* [44] */ 30,
-  /* [45] */ 0,
-  /* [46] */ 1,
-  /* [47] */ 8,
-  /* [48] */ 1,
-  /* [49] */ 2,
-  /* [50] */ 28,
-  /* [51] */ 0,
-  /* [52] */ 1,
-  /* [53] */ 27,
-  /* [54] */ 0,
-  /* [55] */ 1,
-  /* [56] */ 8,
-  /* [57] */ 0,
-  /* [58] */ 3,
-  /* [59] */ 8,
-  /* [60] */ 0,
-  /* [61] */ 4,
-  /* [62] */ 27,
-  /* [63] */ 3,
-  /* [64] */ 10,
-  /* [65] */ 28,
-  /* [66] */ 3,
-  /* [67] */ 10,
-  /* [68] */ 29,
-  /* [69] */ 3,
-  /* [70] */ 10,
-  /* [71] */ 30,
-  /* [72] */ 3,
-  /* [73] */ 10,
-  /* [74] */ 27,
-  /* [75] */ 4,
-  /* [76] */ 10,
-  /* [77] */ 28,
-  /* [78] */ 4,
-  /* [79] */ 10,
-  /* [80] */ 30,
-  /* [81] */ 5,
-  /* [82] */ 10,
-  /* [83] */ 29,
-  /* [84] */ 5,
-  /* [85] */ 10,
-  /* [86] */ 29,
-  /* [87] */ 4,
-  /* [88] */ 10,
-  /* [89] */ 28,
-  /* [90] */ 5,
-  /* [91] */ 10,
-  /* [92] */ 27,
-  /* [93] */ 5,
-  /* [94] */ 10,
-  /* [95] */ 6,
-  /* [96] */ 3,
-  /* [97] */ 7,
-  /* [98] */ 3,
+  /* [40] */ 20,
+  /* [41] */ 0,
+  /* [42] */ 2,
+  /* [43] */ 42,
+  /* [44] */ 5,
+  /* [45] */ 10,
+  /* [46] */ 7,
+  /* [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,
+  /* [64] */ 4,
+  /* [65] */ 10,
+  /* [66] */ 2,
+  /* [67] */ 40,
+  /* [68] */ 4,
+  /* [69] */ 10,
+  /* [70] */ 39,
+  /* [71] */ 4,
+  /* [72] */ 10,
+  /* [73] */ 42,
+  /* [74] */ 3,
+  /* [75] */ 10,
+  /* [76] */ 20,
+  /* [77] */ 1,
+  /* [78] */ 7,
+  /* [79] */ 42,
+  /* [80] */ 0,
+  /* [81] */ 1,
+  /* [82] */ 40,
+  /* [83] */ 0,
+  /* [84] */ 1,
+  /* [85] */ 39,
+  /* [86] */ 0,
+  /* [87] */ 1,
+  /* [88] */ 41,
+  /* [89] */ 3,
+  /* [90] */ 10,
+  /* [91] */ 40,
+  /* [92] */ 3,
+  /* [93] */ 10,
+  /* [94] */ 39,
+  /* [95] */ 3,
+  /* [96] */ 10,
+  /* [97] */ 20,
+  /* [98] */ 0,
   /* [99] */ 5,
-  /* [100] */ 3,
-  /* [101] */ 7,
-  /* [102] */ 4,
-  /* [103] */ 15,
-  /* [104] */ 0,
-  /* [105] */ 7,
+  /* [100] */ 20,
+  /* [101] */ 0,
+  /* [102] */ 6,
+  /* [103] */ 8,
+  /* [104] */ 7,
+  /* [105] */ 8,
   /* [106] */ 0,
-  /* [107] */ 16,
-  /* [108] */ 0,
-  /* [109] */ 7,
-  /* [110] */ 2,
-  /* [111] */ 17,
-  /* [112] */ 0,
-  /* [113] */ 5,
+  /* [107] */ 8,
+  /* [108] */ 1,
+  /* [109] */ 8,
+  /* [110] */ 5,
+  /* [111] */ 8,
+  /* [112] */ 6,
+  /* [113] */ 8,
   /* [114] */ 2,
-  /* [115] */ 33,
+  /* [115] */ 9,
   /* [116] */ 0,
-  /* [117] */ 18,
+  /* [117] */ 45,
   /* [118] */ 0,
-  /* [119] */ 21,
-  /* [120] */ 0,
-  /* [121] */ 6,
-  /* [122] */ 2,
-  /* [123] */ 20,
-  /* [124] */ 2,
-  /* [125] */ 19,
-  /* [126] */ 0,
-  /* [127] */ 19,
+  /* [119] */ 9,
+  /* [120] */ 1,
+  /* [121] */ 9,
+  /* [122] */ 7,
+  /* [123] */ 9,
+  /* [124] */ 5,
+  /* [125] */ 9,
+  /* [126] */ 6,
+  /* [127] */ 9,
   /* [128] */ 2,
-  /* [129] */ 20,
+  /* [129] */ 27,
   /* [130] */ 0,
-  /* [131] */ 18,
-  /* [132] */ 2,
-  /* [133] */ 17,
-  /* [134] */ 2,
-  /* [135] */ 5,
+  /* [131] */ 28,
+  /* [132] */ 0,
+  /* [133] */ 29,
+  /* [134] */ 0,
+  /* [135] */ 47,
   /* [136] */ 0,
-  /* [137] */ 35,
+  /* [137] */ 19,
   /* [138] */ 0,
-  /* [139] */ 16,
-  /* [140] */ 2,
-  /* [141] */ 15,
-  /* [142] */ 2,
-  /* [143] */ 13,
-  /* [144] */ 23,
-  /* [145] */ 24,
-  /* [146] */ 25,
-  /* [147] */ 22,
-  /* [148] */ 26,
-  /* [149] */ 14,
-  /* [150] */ 31,
-  /* [151] */ 32,
-  /* [152] */ 34,
+  /* [139] */ 30,
+  /* [140] */ 0,
+  /* [141] */ 31,
+  /* [142] */ 0,
+  /* [143] */ 32,
+  /* [144] */ 0,
+  /* [145] */ 33,
+  /* [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] */ 18,
+  /* [174] */ 7,
+  /* [175] */ 18,
+  /* [176] */ 0,
+  /* [177] */ 27,
+  /* [178] */ 7,
+  /* [179] */ 28,
+  /* [180] */ 7,
+  /* [181] */ 29,
+  /* [182] */ 7,
+  /* [183] */ 19,
+  /* [184] */ 7,
+  /* [185] */ 30,
+  /* [186] */ 7,
+  /* [187] */ 31,
+  /* [188] */ 7,
+  /* [189] */ 32,
+  /* [190] */ 7,
+  /* [191] */ 25,
+  /* [192] */ 26,
+  /* [193] */ 37,
+  /* [194] */ 36,
+  /* [195] */ 35,
+  /* [196] */ 34,
+  /* [197] */ 43,
+  /* [198] */ 38,
+  /* [199] */ 44,
+  /* [200] */ 46,
 };
 
 // Assert that the MatcherIndex is big enough to index all the matchers, plus
@@ -1838,1363 +2527,1363 @@
 constexpr ParameterInfo kParameters[] = {
   {
     /* [0] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [1] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [2] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [3] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [4] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [5] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [6] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [7] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [8] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [9] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [10] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [11] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [12] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [13] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [14] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [15] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [16] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [17] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [18] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [19] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [20] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [21] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [22] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [23] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [24] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [25] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [26] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [27] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [28] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [29] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [30] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [31] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [32] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [33] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [34] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [35] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [36] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [37] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [38] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [39] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [40] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [41] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [42] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [43] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [44] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [45] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [46] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [47] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [48] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [49] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [50] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [51] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [52] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [53] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [54] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [55] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [56] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [57] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [58] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [59] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [60] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [61] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [62] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [63] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [64] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [65] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[181],
   },
   {
     /* [66] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [67] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [68] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [69] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [70] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [71] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [72] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [73] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [74] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [75] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [76] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [77] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [78] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [79] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [80] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [81] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [82] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [83] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [84] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[179],
   },
   {
     /* [85] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [86] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [87] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [88] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [89] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [90] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [91] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [92] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [93] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [94] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [95] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [96] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [97] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[133],
   },
   {
     /* [98] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [99] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [100] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [101] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [102] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[181],
   },
   {
     /* [103] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [104] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [105] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [106] */
     /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [107] */
     /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [108] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [109] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [110] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [111] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [112] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [113] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [114] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[185],
   },
   {
     /* [115] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [116] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [117] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [118] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
-  },
-  {
-    /* [119] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
-  },
-  {
-    /* [120] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [121] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [122] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [123] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[123],
-  },
-  {
-    /* [124] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [125] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [126] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [127] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [128] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
-  },
-  {
-    /* [129] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [130] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [131] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [132] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [133] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
-  },
-  {
-    /* [134] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
-  },
-  {
-    /* [135] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [136] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [137] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [138] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
-  },
-  {
-    /* [139] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
-  },
-  {
-    /* [140] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
-  },
-  {
-    /* [141] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [142] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [143] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[127],
-  },
-  {
-    /* [144] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [145] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [146] */
     /* usage */ ParameterUsage::kDdx,
     /* matcher indices */ &kMatcherIndices[121],
   },
   {
-    /* [147] */
+    /* [118] */
     /* usage */ ParameterUsage::kDdy,
     /* matcher indices */ &kMatcherIndices[121],
   },
   {
-    /* [148] */
+    /* [119] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[123],
+  },
+  {
+    /* [120] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* matcher indices */ &kMatcherIndices[189],
+  },
+  {
+    /* [121] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [122] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
+  },
+  {
+    /* [123] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [124] */
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[121],
+  },
+  {
+    /* [125] */
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[121],
+  },
+  {
+    /* [126] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[181],
+  },
+  {
+    /* [127] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [128] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [129] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [130] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [131] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
+  },
+  {
+    /* [132] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[181],
+  },
+  {
+    /* [133] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [134] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [135] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [136] */
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [137] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
+  },
+  {
+    /* [138] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
+  },
+  {
+    /* [139] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
+  },
+  {
+    /* [140] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [141] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [142] */
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [143] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
+  },
+  {
+    /* [144] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
+  },
+  {
+    /* [145] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
+  },
+  {
+    /* [146] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [147] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [148] */
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [149] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [150] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [151] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [152] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [153] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [154] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [155] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [156] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [157] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [158] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [159] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [160] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
   },
   {
     /* [161] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [162] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [163] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [164] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [165] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[189],
   },
   {
     /* [166] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [167] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [168] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [169] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [170] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
   },
   {
     /* [171] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [172] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [173] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [174] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [175] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[185],
   },
   {
     /* [176] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [177] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [178] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [179] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[123],
   },
   {
     /* [180] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[179],
   },
   {
     /* [181] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [182] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [183] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [184] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [185] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [186] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [187] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [188] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [189] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [190] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[181],
   },
   {
     /* [191] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [192] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [193] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [194] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [195] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[179],
   },
   {
     /* [196] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [197] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [198] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [199] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[129],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [200] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [201] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [202] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [203] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [204] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[107],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [205] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [206] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [207] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [208] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [209] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [210] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[187],
   },
   {
     /* [211] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [212] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [213] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [214] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [215] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[181],
   },
   {
     /* [216] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [217] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [218] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [219] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [220] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[185],
   },
   {
     /* [221] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [222] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[121],
   },
   {
-    /* [222] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
     /* [223] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[107],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [224] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [225] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [226] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [227] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [228] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [229] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [230] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [231] */
-    /* usage */ ParameterUsage::kSampler,
+    /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[143],
   },
   {
     /* [232] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [233] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [234] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [235] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [236] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[133],
   },
   {
     /* [237] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [238] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[145],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [239] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [240] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[179],
+  },
+  {
+    /* [241] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [242] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [243] */
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [244] */
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [245] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
+  },
+  {
+    /* [246] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
+  },
+  {
+    /* [247] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[121],
   },
   {
-    /* [241] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [242] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
-  },
-  {
-    /* [243] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
-  },
-  {
-    /* [244] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
-  },
-  {
-    /* [245] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [246] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
-  },
-  {
-    /* [247] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
-  },
-  {
     /* [248] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [249] */
     /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [250] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[68],
+    /* matcher indices */ &kMatcherIndices[185],
   },
   {
     /* [251] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [252] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [253] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [254] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[145],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[123],
   },
   {
     /* [255] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [256] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [257] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [258] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
-  },
-  {
-    /* [259] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
-  },
-  {
-    /* [260] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
-  },
-  {
-    /* [261] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [262] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[145],
-  },
-  {
-    /* [263] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[149],
-  },
-  {
-    /* [264] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [265] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [266] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[127],
-  },
-  {
-    /* [267] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [268] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [269] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [270] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[131],
   },
   {
+    /* [257] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [258] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [259] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
+  },
+  {
+    /* [260] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
+  },
+  {
+    /* [261] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
+  },
+  {
+    /* [262] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [263] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [264] */
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [265] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
+  },
+  {
+    /* [266] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
+  },
+  {
+    /* [267] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [268] */
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [269] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
+  },
+  {
+    /* [270] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[189],
+  },
+  {
     /* [271] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [272] */
@@ -3203,1928 +3892,1928 @@
   },
   {
     /* [273] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [274] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [275] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
   },
   {
     /* [276] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [277] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [278] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[127],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [279] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [280] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [281] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [282] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[145],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [283] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [284] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [285] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [286] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [287] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [288] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [289] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [290] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
+    /* matcher indices */ &kMatcherIndices[181],
   },
   {
     /* [291] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [292] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [293] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [294] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [295] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [296] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [297] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [298] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [299] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [300] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [301] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [302] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [303] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [304] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [305] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [306] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[83],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [307] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[187],
   },
   {
     /* [308] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [309] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[101],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [310] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [311] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [312] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [313] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [314] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [315] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[185],
   },
   {
     /* [316] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [317] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [318] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
-  },
-  {
-    /* [319] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [320] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[121],
   },
   {
+    /* [318] */
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [319] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[179],
+  },
+  {
+    /* [320] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
     /* [321] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [322] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[86],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [323] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
-  },
-  {
-    /* [324] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [325] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[97],
-  },
-  {
-    /* [326] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
-  },
-  {
-    /* [327] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [328] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
-  },
-  {
-    /* [329] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[15],
-  },
-  {
-    /* [330] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
-  },
-  {
-    /* [331] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [332] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
-  },
-  {
-    /* [333] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[99],
-  },
-  {
-    /* [334] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[133],
   },
   {
-    /* [335] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [336] */
+    /* [324] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
-    /* [337] */
+    /* [325] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
-    /* [338] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* [326] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
-    /* [339] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
-  },
-  {
-    /* [340] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [341] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [342] */
+    /* [327] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
+    /* matcher indices */ &kMatcherIndices[185],
   },
   {
-    /* [343] */
+    /* [328] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
-    /* [344] */
+    /* [329] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[121],
   },
   {
-    /* [345] */
+    /* [330] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [331] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[179],
+  },
+  {
+    /* [332] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [333] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [334] */
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [335] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
+  },
+  {
+    /* [336] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [337] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
+  },
+  {
+    /* [338] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [339] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[187],
+  },
+  {
+    /* [340] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [341] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
+  },
+  {
+    /* [342] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [343] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
+  },
+  {
+    /* [344] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [345] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [346] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [347] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [348] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [349] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [350] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[74],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [351] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[189],
   },
   {
     /* [352] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[97],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [353] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [354] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [355] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[185],
   },
   {
     /* [356] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [357] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [358] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[123],
   },
   {
     /* [359] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [360] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[131],
   },
   {
     /* [361] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [362] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [363] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[181],
   },
   {
     /* [364] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [365] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [366] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [367] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[179],
   },
   {
     /* [368] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [369] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [370] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [371] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [372] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [373] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [374] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[145],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [375] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [376] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [377] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
-  },
-  {
-    /* [378] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [379] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[121],
   },
   {
+    /* [378] */
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [379] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
+  },
+  {
     /* [380] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [381] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [382] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [383] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[88],
   },
   {
     /* [384] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [385] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [386] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [387] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[63],
   },
   {
     /* [388] */
-    /* usage */ ParameterUsage::kSampleIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [389] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [390] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[57],
   },
   {
     /* [391] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [392] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [393] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [394] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [395] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
   },
   {
     /* [396] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [397] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [398] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [399] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[47],
   },
   {
     /* [400] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [401] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [402] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[61],
   },
   {
     /* [403] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [404] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [405] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [406] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [407] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [408] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [409] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [410] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [411] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [412] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [413] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[119],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [414] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [415] */
-    /* usage */ ParameterUsage::kSampleIndex,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [416] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [417] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [418] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [419] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[145],
-  },
-  {
-    /* [420] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [421] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [422] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[107],
-  },
-  {
-    /* [423] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
-  },
-  {
-    /* [424] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [425] */
-    /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[103],
   },
   {
-    /* [426] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* [418] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
-    /* [427] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* [419] */
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [428] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[80],
+    /* [420] */
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [429] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* [421] */
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [430] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[101],
+    /* [422] */
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [431] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* [423] */
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
-    /* [432] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [433] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
-  },
-  {
-    /* [434] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[127],
-  },
-  {
-    /* [435] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [436] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[121],
-  },
-  {
-    /* [437] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
-  },
-  {
-    /* [438] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
-  },
-  {
-    /* [439] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
-  },
-  {
-    /* [440] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[89],
-  },
-  {
-    /* [441] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
-  },
-  {
-    /* [442] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[101],
-  },
-  {
-    /* [443] */
+    /* [424] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[141],
   },
   {
-    /* [444] */
+    /* [425] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
-    /* [445] */
+    /* [426] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
-    /* [446] */
-    /* usage */ ParameterUsage::kTexture,
+    /* [427] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [428] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [429] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [430] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [431] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[105],
+  },
+  {
+    /* [432] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[105],
+  },
+  {
+    /* [433] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[105],
+  },
+  {
+    /* [434] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[105],
+  },
+  {
+    /* [435] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[115],
+  },
+  {
+    /* [436] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[115],
+  },
+  {
+    /* [437] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[115],
+  },
+  {
+    /* [438] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[115],
+  },
+  {
+    /* [439] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [440] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [441] */
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
+    /* [442] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
+    /* [443] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[49],
+  },
+  {
+    /* [444] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[49],
+  },
+  {
+    /* [445] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[49],
+  },
+  {
+    /* [446] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[49],
+  },
+  {
     /* [447] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [448] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [449] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [450] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [451] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [452] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [453] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [454] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[97],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [455] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [456] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[197],
   },
   {
     /* [457] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [458] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[77],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [459] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [460] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[97],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [461] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[65],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [462] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [463] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [464] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [465] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [466] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [467] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [468] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[49],
   },
   {
     /* [469] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[49],
   },
   {
     /* [470] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[49],
   },
   {
     /* [471] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[115],
   },
   {
     /* [472] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[35],
+    /* matcher indices */ &kMatcherIndices[115],
   },
   {
     /* [473] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[71],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[115],
   },
   {
     /* [474] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [475] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [476] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[92],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [477] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[94],
   },
   {
     /* [478] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[101],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [479] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [480] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[59],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[91],
   },
   {
     /* [481] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [482] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [483] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[73],
   },
   {
     /* [484] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[123],
   },
   {
     /* [485] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [486] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[70],
   },
   {
     /* [487] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [488] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[57],
   },
   {
     /* [489] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[67],
   },
   {
     /* [490] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [491] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[57],
   },
   {
     /* [492] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[59],
   },
   {
     /* [493] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[123],
   },
   {
     /* [494] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[99],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[57],
   },
   {
     /* [495] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [496] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [497] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [498] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [499] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [500] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [501] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [502] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [503] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* usage */ ParameterUsage::kZw,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [504] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [505] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [506] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [507] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [508] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [509] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [510] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[56],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [511] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* usage */ ParameterUsage::kYz,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [512] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [513] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [514] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [515] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [516] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [517] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [518] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[61],
   },
   {
     /* [519] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[51],
   },
   {
     /* [520] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [521] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[61],
   },
   {
     /* [522] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [523] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [524] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [525] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [526] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [527] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [528] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [529] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[16],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [530] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[16],
+    /* matcher indices */ &kMatcherIndices[40],
   },
   {
     /* [531] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [532] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [533] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [534] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [535] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [536] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [537] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[107],
+    /* matcher indices */ &kMatcherIndices[43],
   },
   {
     /* [538] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[123],
   },
   {
     /* [539] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[61],
   },
   {
     /* [540] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
-  },
-  {
-    /* [541] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[111],
-  },
-  {
-    /* [542] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [543] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
-  },
-  {
-    /* [544] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
-  },
-  {
-    /* [545] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[117],
-  },
-  {
-    /* [546] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [547] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[16],
-  },
-  {
-    /* [548] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[16],
-  },
-  {
-    /* [549] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[125],
-  },
-  {
-    /* [550] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
-  },
-  {
-    /* [551] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
-  },
-  {
-    /* [552] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
-  },
-  {
-    /* [553] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[129],
   },
   {
-    /* [554] */
+    /* [541] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [542] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [543] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[131],
+  },
+  {
+    /* [544] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
+  },
+  {
+    /* [545] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [546] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
+  },
+  {
+    /* [547] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
+  },
+  {
+    /* [548] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [549] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [550] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [551] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [552] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [553] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [554] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [555] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [556] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [557] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [558] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [559] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [560] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [561] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [562] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[16],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [563] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [564] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[187],
   },
   {
     /* [565] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[16],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [566] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [567] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[145],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [568] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [569] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[16],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [570] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[185],
   },
   {
     /* [571] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [572] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[58],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [573] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[47],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[139],
   },
   {
     /* [574] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[16],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[123],
   },
   {
     /* [575] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[24],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [576] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[145],
   },
   {
     /* [577] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [578] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kSampleIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [579] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [580] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [581] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [582] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[179],
   },
   {
     /* [583] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [584] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[103],
   },
   {
     /* [585] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[177],
   },
   {
     /* [586] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [587] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [588] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [589] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [590] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kSampleIndex,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [591] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [592] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [593] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [594] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [595] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[191],
   },
   {
     /* [596] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [597] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [598] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [599] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [600] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [601] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [602] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [603] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [604] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [605] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [606] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [607] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [608] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [609] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [610] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [611] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[129],
   },
   {
     /* [612] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [613] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[35],
+    /* matcher indices */ &kMatcherIndices[40],
   },
   {
     /* [614] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[35],
+    /* matcher indices */ &kMatcherIndices[40],
   },
   {
     /* [615] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[131],
   },
   {
     /* [616] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [617] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [618] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [619] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[35],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[133],
   },
   {
     /* [620] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[35],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [621] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [622] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [623] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[139],
   },
   {
     /* [624] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [625] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [626] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [627] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[141],
   },
   {
     /* [628] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [629] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[40],
   },
   {
     /* [630] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[40],
   },
   {
     /* [631] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[143],
   },
   {
     /* [632] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [633] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [634] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[21],
   },
   {
     /* [635] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [636] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [637] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [638] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [639] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [640] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [641] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
   },
   {
     /* [642] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [643] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [644] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [645] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [646] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [647] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [648] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [649] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
   },
   {
     /* [650] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[44],
   },
   {
     /* [651] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [652] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [653] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [654] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [655] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [656] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [657] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [658] */
@@ -5134,17 +5823,17 @@
   {
     /* [659] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [660] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[59],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [661] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [662] */
@@ -5154,212 +5843,212 @@
   {
     /* [663] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[19],
   },
   {
     /* [664] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[23],
   },
   {
     /* [665] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[76],
   },
   {
     /* [666] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [667] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [668] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[119],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [669] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [670] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[145],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [671] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [672] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [673] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[129],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [674] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [675] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [676] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [677] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[107],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [678] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[16],
-  },
-  {
-    /* [679] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
-  },
-  {
-    /* [680] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[41],
-  },
-  {
-    /* [681] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
-  },
-  {
-    /* [682] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
-  },
-  {
-    /* [683] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[129],
-  },
-  {
-    /* [684] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[111],
-  },
-  {
-    /* [685] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
-  },
-  {
-    /* [686] */
-    /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[44],
   },
   {
+    /* [679] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [680] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [681] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [682] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [683] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [684] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [685] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [686] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
     /* [687] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [688] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[50],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [689] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[53],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [690] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [691] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[146],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [692] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[145],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [693] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [694] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[147],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [695] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[119],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [696] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[129],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [697] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [698] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [699] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [700] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[107],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [701] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [702] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [703] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [704] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [705] */
@@ -5379,544 +6068,1519 @@
   {
     /* [708] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [709] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [710] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [711] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [712] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [713] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [714] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [715] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [716] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [717] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [718] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [719] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [720] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [721] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [722] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [723] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [724] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [725] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [726] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [727] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[197],
   },
   {
     /* [728] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[109],
   },
   {
     /* [729] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [730] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [731] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [732] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [733] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[113],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [734] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [735] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [736] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [737] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [738] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [739] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [740] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[2],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [741] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [742] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [743] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [744] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [745] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [746] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [747] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [748] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [749] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [750] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [751] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [752] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[35],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [753] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [754] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[100],
   },
   {
     /* [755] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [756] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [757] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [758] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[100],
   },
   {
     /* [759] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [760] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [761] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [762] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [763] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [764] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [765] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [766] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* usage */ ParameterUsage::kYz,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [767] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [768] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [769] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [770] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kZw,
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [771] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [772] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [773] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [774] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [775] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kXyz,
+    /* matcher indices */ &kMatcherIndices[115],
   },
   {
     /* [776] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [777] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [778] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* usage */ ParameterUsage::kZyw,
+    /* matcher indices */ &kMatcherIndices[115],
   },
   {
     /* [779] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [780] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[105],
   },
   {
     /* [781] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[115],
   },
   {
     /* [782] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[115],
   },
   {
     /* [783] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[49],
   },
   {
     /* [784] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* matcher indices */ &kMatcherIndices[49],
   },
   {
     /* [785] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [786] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [787] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [788] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [789] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [790] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [791] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [792] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[3],
+    /* matcher indices */ &kMatcherIndices[121],
   },
   {
     /* [793] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [794] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [795] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [796] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [797] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [798] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [799] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [800] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [801] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [802] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* matcher indices */ &kMatcherIndices[97],
   },
   {
     /* [803] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[7],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [804] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[35],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [805] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [806] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[35],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [807] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [808] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[32],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [809] */
     /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[15],
   },
+  {
+    /* [810] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [811] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [812] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [813] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [814] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [815] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [816] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [817] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [818] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [819] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [820] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [821] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [822] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [823] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [824] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [825] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [826] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [827] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [828] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [829] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [830] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [831] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [832] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [833] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [834] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [835] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [836] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [837] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [838] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [839] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [840] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
+  {
+    /* [841] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [842] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [843] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [844] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[198],
+  },
+  {
+    /* [845] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[145],
+  },
+  {
+    /* [846] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
+  },
+  {
+    /* [847] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
+  },
+  {
+    /* [848] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
+  },
+  {
+    /* [849] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
+  },
+  {
+    /* [850] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
+  },
+  {
+    /* [851] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[143],
+  },
+  {
+    /* [852] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[141],
+  },
+  {
+    /* [853] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[139],
+  },
+  {
+    /* [854] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[133],
+  },
+  {
+    /* [855] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[131],
+  },
+  {
+    /* [856] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[129],
+  },
+  {
+    /* [857] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
+  {
+    /* [858] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[21],
+  },
+  {
+    /* [859] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[40],
+  },
+  {
+    /* [860] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [861] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [862] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [863] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [864] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
+  },
+  {
+    /* [865] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
+  },
+  {
+    /* [866] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[143],
+  },
+  {
+    /* [867] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[133],
+  },
+  {
+    /* [868] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [869] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [870] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [871] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [872] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [873] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [874] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [875] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [876] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [877] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [878] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [879] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [880] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [881] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [882] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [883] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[197],
+  },
+  {
+    /* [884] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[79],
+  },
+  {
+    /* [885] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [886] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[82],
+  },
+  {
+    /* [887] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[85],
+  },
+  {
+    /* [888] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[198],
+  },
+  {
+    /* [889] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[193],
+  },
+  {
+    /* [890] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
+  },
+  {
+    /* [891] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[195],
+  },
+  {
+    /* [892] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[196],
+  },
+  {
+    /* [893] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[145],
+  },
+  {
+    /* [894] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[143],
+  },
+  {
+    /* [895] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[141],
+  },
+  {
+    /* [896] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[139],
+  },
+  {
+    /* [897] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[133],
+  },
+  {
+    /* [898] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[131],
+  },
+  {
+    /* [899] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[129],
+  },
+  {
+    /* [900] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [901] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
+    /* [902] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
+    /* [903] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
+    /* [904] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
+    /* [905] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [906] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [907] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [908] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[11],
+  },
+  {
+    /* [909] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [910] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [911] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [912] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [913] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [914] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [915] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [916] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [917] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [918] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [919] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[183],
+  },
+  {
+    /* [920] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[44],
+  },
+  {
+    /* [921] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [922] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [923] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
+    /* [924] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [925] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [926] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [927] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [928] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [929] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[21],
+  },
+  {
+    /* [930] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [931] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[173],
+  },
+  {
+    /* [932] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[105],
+  },
+  {
+    /* [933] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [934] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [935] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[107],
+  },
+  {
+    /* [936] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[107],
+  },
+  {
+    /* [937] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[107],
+  },
+  {
+    /* [938] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[107],
+  },
+  {
+    /* [939] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[21],
+  },
+  {
+    /* [940] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[115],
+  },
+  {
+    /* [941] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [942] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [943] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [944] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [945] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[119],
+  },
+  {
+    /* [946] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[119],
+  },
+  {
+    /* [947] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[119],
+  },
+  {
+    /* [948] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[119],
+  },
+  {
+    /* [949] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[40],
+  },
+  {
+    /* [950] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[49],
+  },
+  {
+    /* [951] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [952] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [953] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[37],
+  },
+  {
+    /* [954] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [955] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [956] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [957] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [958] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[45],
+  },
+  {
+    /* [959] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[53],
+  },
+  {
+    /* [960] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[53],
+  },
+  {
+    /* [961] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[53],
+  },
+  {
+    /* [962] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[53],
+  },
+  {
+    /* [963] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [964] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[4],
+  },
+  {
+    /* [965] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [966] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [967] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [968] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[169],
+  },
+  {
+    /* [969] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[149],
+  },
+  {
+    /* [970] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [971] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [972] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [973] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [974] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[153],
+  },
+  {
+    /* [975] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [976] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[5],
+  },
+  {
+    /* [977] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[103],
+  },
+  {
+    /* [978] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[21],
+  },
+  {
+    /* [979] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[157],
+  },
+  {
+    /* [980] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [981] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[6],
+  },
+  {
+    /* [982] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
+  },
+  {
+    /* [983] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [984] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[161],
+  },
+  {
+    /* [985] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [986] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[40],
+  },
+  {
+    /* [987] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
+    /* [988] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[165],
+  },
+  {
+    /* [989] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[45],
+  },
 };
 
-constexpr OpenTypeInfo kOpenTypes[] = {
+constexpr TemplateTypeInfo kTemplateTypes[] = {
   {
     /* [0] */
     /* name */ "T",
-    /* matcher index */ 36,
+    /* matcher index */ 2,
   },
   {
     /* [1] */
-    /* name */ "T",
-    /* matcher index */ 38,
+    /* name */ "U",
+    /* matcher index */ 57,
   },
   {
     /* [2] */
     /* name */ "T",
-    /* matcher index */ kNoMatcher,
+    /* matcher index */ 7,
   },
   {
     /* [3] */
-    /* name */ "T",
-    /* matcher index */ 39,
+    /* name */ "U",
+    /* matcher index */ 54,
   },
   {
     /* [4] */
     /* name */ "T",
-    /* matcher index */ 37,
+    /* matcher index */ 5,
+  },
+  {
+    /* [5] */
+    /* name */ "U",
+    /* matcher index */ 55,
+  },
+  {
+    /* [6] */
+    /* name */ "T",
+    /* matcher index */ 6,
+  },
+  {
+    /* [7] */
+    /* name */ "U",
+    /* matcher index */ 56,
+  },
+  {
+    /* [8] */
+    /* name */ "T",
+    /* matcher index */ 48,
+  },
+  {
+    /* [9] */
+    /* name */ "f32",
+    /* matcher index */ kNoMatcher,
+  },
+  {
+    /* [10] */
+    /* name */ "T",
+    /* matcher index */ 53,
+  },
+  {
+    /* [11] */
+    /* name */ "T",
+    /* matcher index */ 50,
+  },
+  {
+    /* [12] */
+    /* name */ "T",
+    /* matcher index */ 52,
+  },
+  {
+    /* [13] */
+    /* name */ "T",
+    /* matcher index */ 51,
+  },
+  {
+    /* [14] */
+    /* name */ "T",
+    /* matcher index */ kNoMatcher,
+  },
+  {
+    /* [15] */
+    /* name */ "T",
+    /* matcher index */ 57,
+  },
+  {
+    /* [16] */
+    /* name */ "T",
+    /* matcher index */ 54,
+  },
+  {
+    /* [17] */
+    /* name */ "T",
+    /* matcher index */ 56,
+  },
+  {
+    /* [18] */
+    /* name */ "T",
+    /* matcher index */ 55,
+  },
+  {
+    /* [19] */
+    /* name */ "T",
+    /* matcher index */ 49,
   },
 };
 
-constexpr OpenNumberInfo kOpenNumbers[] = {
+constexpr TemplateNumberInfo kTemplateNumbers[] = {
   {
     /* [0] */
     /* name */ "K",
@@ -5973,4274 +7637,4897 @@
   {
     /* [0] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[701],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[899],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [1] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[533],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[611],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [2] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[700],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[898],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [3] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[537],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[615],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [4] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[699],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[897],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [5] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[541],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[619],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [6] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[698],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[896],
+    /* return matcher indices */ &kMatcherIndices[123],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [7] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[545],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[623],
+    /* return matcher indices */ &kMatcherIndices[123],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [8] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[697],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[895],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [9] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[549],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[627],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [10] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[696],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[894],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [11] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[553],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[631],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [12] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[695],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[893],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [13] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[694],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[892],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [14] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[559],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[637],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [15] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[693],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[891],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [16] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[563],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[641],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [17] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[692],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[890],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [18] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[567],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[645],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [19] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[691],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[889],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [20] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[571],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[649],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [21] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[690],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[888],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [22] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[3],
-    /* parameters */ &kParameters[689],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[3],
+    /* parameters */ &kParameters[887],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [23] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[3],
-    /* parameters */ &kParameters[688],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[3],
+    /* parameters */ &kParameters[886],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [24] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[3],
-    /* parameters */ &kParameters[687],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[3],
+    /* parameters */ &kParameters[840],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [25] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[3],
-    /* parameters */ &kParameters[686],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[3],
+    /* parameters */ &kParameters[884],
+    /* return matcher indices */ &kMatcherIndices[123],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [26] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[685],
-    /* return matcher indices */ &kMatcherIndices[99],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[883],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [27] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[326],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[585],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [28] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[183],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[582],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [29] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[193],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[367],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [30] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[19],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[363],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [31] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[342],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[215],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [32] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[213],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[570],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [33] */
     /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[278],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[355],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [34] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[108],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[564],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [35] */
     /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[274],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[351],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [36] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[168],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[546],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [37] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[173],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[347],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [38] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[31],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[343],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [39] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[282],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[200],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [40] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[128],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[534],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [41] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[437],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[335],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [42] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[443],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[319],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [43] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[455],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[195],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [44] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[330],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[190],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [45] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[334],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[126],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [46] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[148],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[327],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [47] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[377],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[175],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [48] */
     /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[318],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[339],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [49] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[434],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[165],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [50] */
     /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[314],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[371],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [51] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[431],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[155],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [52] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[302],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[150],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [53] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[298],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[72],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [54] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[78],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* 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),
   },
   {
     /* [55] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[419],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[170],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [56] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[290],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[456],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [57] */
-    /* num parameters */ 4,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[222],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [58] */
-    /* num parameters */ 5,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[203],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[950],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [59] */
-    /* num parameters */ 5,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[208],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[951],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [60] */
-    /* num parameters */ 6,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[49],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[419],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [61] */
-    /* num parameters */ 4,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[218],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[513],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [62] */
-    /* num parameters */ 5,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[198],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[510],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [63] */
     /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[371],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[501],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [64] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[226],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[769],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [65] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[230],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[775],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [66] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[178],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[777],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [67] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[374],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[959],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [68] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[234],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[4],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[960],
+    /* return matcher indices */ &kMatcherIndices[57],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [69] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[446],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[6],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[961],
+    /* return matcher indices */ &kMatcherIndices[61],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [70] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[461],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[962],
+    /* return matcher indices */ &kMatcherIndices[65],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [71] */
     /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[250],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[359],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [72] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[473],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[255],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [73] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[350],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[235],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [74] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[458],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[96],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [75] */
     /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[322],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[423],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [76] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[452],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[230],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [77] */
     /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[476],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[447],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [78] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[440],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[415],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [79] */
     /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[306],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[411],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [80] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[428],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[225],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [81] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[679],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[594],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [82] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[677],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[395],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [83] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[676],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[477],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [84] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[675],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[480],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [85] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[674],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[383],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [86] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[673],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[483],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [87] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[672],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[486],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [88] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[671],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[489],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [89] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[670],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[387],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [90] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[669],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[492],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [91] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[665],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[516],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [92] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[551],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[519],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [93] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[555],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[399],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [94] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[557],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[537],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [95] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[561],
-    /* return matcher indices */ &kMatcherIndices[16],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[115],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [96] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[565],
-    /* return matcher indices */ &kMatcherIndices[16],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[940],
+    /* return matcher indices */ &kMatcherIndices[115],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [97] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[1],
-    /* parameters */ &kParameters[569],
-    /* return matcher indices */ &kMatcherIndices[47],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[941],
+    /* return matcher indices */ &kMatcherIndices[115],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [98] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[1],
-    /* parameters */ &kParameters[573],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[531],
+    /* return matcher indices */ &kMatcherIndices[115],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [99] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 3,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[0],
-    /* parameters */ &kParameters[575],
-    /* return matcher indices */ &kMatcherIndices[28],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[763],
+    /* return matcher indices */ &kMatcherIndices[115],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [100] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[425],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[765],
+    /* return matcher indices */ &kMatcherIndices[115],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [101] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[422],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[945],
+    /* return matcher indices */ &kMatcherIndices[121],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [102] */
-    /* num parameters */ 4,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[294],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[4],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[946],
+    /* return matcher indices */ &kMatcherIndices[123],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [103] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[416],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[6],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[947],
+    /* return matcher indices */ &kMatcherIndices[125],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [104] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[413],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[948],
+    /* return matcher indices */ &kMatcherIndices[127],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [105] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[410],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[856],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [106] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[286],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[855],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [107] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[386],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[854],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [108] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[493],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[853],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [109] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[103],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[852],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [110] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[67],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[851],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [111] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[7],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[850],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [112] */
-    /* num parameters */ 7,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[0],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[849],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [113] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[93],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[848],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [114] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[43],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* 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[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [115] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[143],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[540],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [116] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[25],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[543],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [117] */
     /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[346],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[323],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [118] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[88],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[573],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [119] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[113],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[576],
+    /* return matcher indices */ &kMatcherIndices[49],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [120] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[55],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[579],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [121] */
     /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[270],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[407],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [122] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[73],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[588],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [123] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[266],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[727],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [124] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[123],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[685],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [125] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[4],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[755],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[681],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [126] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[4],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[756],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[679],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [127] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[521],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[673],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [128] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[523],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[671],
+    /* return matcher indices */ &kMatcherIndices[11],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [129] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[525],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[669],
+    /* return matcher indices */ &kMatcherIndices[11],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [130] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[527],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[1],
+    /* parameters */ &kParameters[667],
+    /* return matcher indices */ &kMatcherIndices[76],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [131] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[529],
-    /* return matcher indices */ &kMatcherIndices[16],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[1],
+    /* parameters */ &kParameters[665],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [132] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[246],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 3,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[0],
+    /* parameters */ &kParameters[663],
+    /* return matcher indices */ &kMatcherIndices[27],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [133] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[153],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[331],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [134] */
     /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[138],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[180],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [135] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[37],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[290],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [136] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[262],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[132],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [137] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[118],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[315],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [138] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[258],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[250],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [139] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[188],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[307],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [140] */
     /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[98],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[270],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [141] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[13],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[240],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [142] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[238],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[84],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [143] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[163],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[102],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [144] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[242],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 7,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[65],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [145] */
     /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[158],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[220],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [146] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[83],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[114],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [147] */
-    /* num parameters */ 6,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[61],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[210],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [148] */
-    /* num parameters */ 4,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[254],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[120],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [149] */
-    /* num parameters */ 5,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[133],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[105],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [150] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[531],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[932],
+    /* return matcher indices */ &kMatcherIndices[105],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [151] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[535],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[933],
+    /* return matcher indices */ &kMatcherIndices[105],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [152] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[539],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[759],
+    /* return matcher indices */ &kMatcherIndices[105],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [153] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[543],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[935],
+    /* return matcher indices */ &kMatcherIndices[103],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [154] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[547],
-    /* return matcher indices */ &kMatcherIndices[16],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[4],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[936],
+    /* return matcher indices */ &kMatcherIndices[109],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [155] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[684],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[6],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[937],
+    /* return matcher indices */ &kMatcherIndices[111],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [156] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[683],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[938],
+    /* return matcher indices */ &kMatcherIndices[113],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [157] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[682],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[391],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [158] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[681],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[205],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [159] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[3],
-    /* parameters */ &kParameters[680],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[185],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [160] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[587],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[138],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [161] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[589],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[375],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [162] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[591],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[160],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [163] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[593],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[303],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [164] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[609],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[265],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [165] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[613],
-    /* return matcher indices */ &kMatcherIndices[35],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[260],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [166] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[615],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[144],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [167] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[663],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[311],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [168] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[617],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[245],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [169] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[619],
-    /* return matcher indices */ &kMatcherIndices[35],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[299],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [170] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[621],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[280],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [171] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[623],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[285],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [172] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[577],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[108],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [173] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[579],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[295],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [174] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[583],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[275],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [175] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[585],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[149],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [176] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[401],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[9],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[969],
+    /* return matcher indices */ &kMatcherIndices[149],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [177] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[404],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[970],
+    /* return matcher indices */ &kMatcherIndices[151],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [178] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[407],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[90],
+    /* return matcher indices */ &kMatcherIndices[151],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [179] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[3],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[464],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[781],
+    /* return matcher indices */ &kMatcherIndices[151],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [180] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[3],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[467],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[165],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [181] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[3],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[470],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[9],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[988],
+    /* return matcher indices */ &kMatcherIndices[165],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [182] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[766],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[983],
+    /* return matcher indices */ &kMatcherIndices[167],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [183] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[765],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 12,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[16],
+    /* return matcher indices */ &kMatcherIndices[167],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [184] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[781],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[468],
+    /* return matcher indices */ &kMatcherIndices[167],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [185] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[780],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[169],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [186] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[779],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[9],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[968],
+    /* return matcher indices */ &kMatcherIndices[169],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [187] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[778],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[957],
+    /* return matcher indices */ &kMatcherIndices[171],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [188] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[809],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 8,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[57],
+    /* return matcher indices */ &kMatcherIndices[171],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [189] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[777],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[431],
+    /* return matcher indices */ &kMatcherIndices[171],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [190] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[776],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [191] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[775],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[9],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[964],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [192] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[774],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[965],
+    /* return matcher indices */ &kMatcherIndices[147],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [193] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[773],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[427],
+    /* return matcher indices */ &kMatcherIndices[147],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [194] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[772],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[779],
+    /* return matcher indices */ &kMatcherIndices[147],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [195] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[771],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[157],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [196] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[770],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[9],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[979],
+    /* return matcher indices */ &kMatcherIndices[157],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [197] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[769],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[980],
+    /* return matcher indices */ &kMatcherIndices[159],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [198] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[3],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[633],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 6,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[78],
+    /* return matcher indices */ &kMatcherIndices[159],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [199] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[3],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[639],
-    /* return matcher indices */ &kMatcherIndices[35],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[474],
+    /* return matcher indices */ &kMatcherIndices[159],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [200] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[3],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[629],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[153],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [201] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[3],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[631],
-    /* return matcher indices */ &kMatcherIndices[35],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[9],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[974],
+    /* return matcher indices */ &kMatcherIndices[153],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [202] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[768],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[975],
+    /* return matcher indices */ &kMatcherIndices[155],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [203] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[767],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 8,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[49],
+    /* return matcher indices */ &kMatcherIndices[155],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [204] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[783],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* return matcher indices */ &kMatcherIndices[155],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [205] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[782],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[867],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [206] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[764],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[866],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [207] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[763],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[865],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [208] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[359],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[864],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [209] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[356],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[3],
+    /* parameters */ &kParameters[857],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [210] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[762],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[173],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [211] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[761],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[9],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[931],
+    /* return matcher indices */ &kMatcherIndices[173],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [212] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[760],
-    /* return matcher indices */ &kMatcherIndices[152],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[928],
+    /* return matcher indices */ &kMatcherIndices[175],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [213] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[759],
-    /* return matcher indices */ &kMatcherIndices[137],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 12,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[28],
+    /* return matcher indices */ &kMatcherIndices[175],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [214] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[758],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[435],
+    /* return matcher indices */ &kMatcherIndices[175],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [215] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[757],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[695],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [216] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[750],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[693],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [217] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[749],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[691],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [218] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[748],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[689],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [219] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[747],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[687],
+    /* return matcher indices */ &kMatcherIndices[11],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [220] */
-    /* num parameters */ 4,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[338],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[705],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [221] */
-    /* num parameters */ 4,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[310],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[703],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [222] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[746],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* 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),
   },
   {
     /* [223] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[745],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[699],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [224] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[507],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[697],
+    /* return matcher indices */ &kMatcherIndices[11],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [225] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[509],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[183],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [226] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[641],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[9],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[919],
+    /* return matcher indices */ &kMatcherIndices[183],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [227] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[643],
-    /* return matcher indices */ &kMatcherIndices[35],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[912],
+    /* return matcher indices */ &kMatcherIndices[137],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [228] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[742],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 16,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[0],
+    /* return matcher indices */ &kMatcherIndices[137],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [229] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[741],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[443],
+    /* return matcher indices */ &kMatcherIndices[137],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [230] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[739],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[161],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [231] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[738],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[9],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[984],
+    /* return matcher indices */ &kMatcherIndices[161],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [232] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[491],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[985],
+    /* return matcher indices */ &kMatcherIndices[163],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [233] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[581],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 9,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[40],
+    /* return matcher indices */ &kMatcherIndices[163],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [234] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[645],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[471],
+    /* return matcher indices */ &kMatcherIndices[163],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [235] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[647],
-    /* return matcher indices */ &kMatcherIndices[35],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[633],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [236] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[649],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[629],
+    /* return matcher indices */ &kMatcherIndices[40],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [237] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[651],
-    /* return matcher indices */ &kMatcherIndices[35],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[625],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [238] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[736],
-    /* return matcher indices */ &kMatcherIndices[151],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[621],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [239] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[735],
-    /* return matcher indices */ &kMatcherIndices[115],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[653],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [240] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[603],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[651],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [241] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[601],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[647],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [242] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[653],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[643],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [243] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[655],
-    /* return matcher indices */ &kMatcherIndices[35],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[661],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [244] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[605],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[659],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [245] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[607],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[657],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [246] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[786],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[655],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [247] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[785],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[617],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [248] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[744],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[613],
+    /* return matcher indices */ &kMatcherIndices[40],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [249] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[743],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[609],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [250] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[737],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[607],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [251] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[702],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [252] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[485],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[920],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [253] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[483],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[18],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[921],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [254] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[728],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [255] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[727],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[929],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [256] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[788],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[15],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[930],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [257] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[787],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[522],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [258] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[790],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[525],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [259] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[789],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[528],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [260] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[726],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [261] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[725],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[923],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [262] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[724],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[17],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[924],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [263] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[723],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [264] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[489],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[926],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [265] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[487],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[16],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[927],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [266] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[722],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[459],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [267] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[721],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[462],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [268] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[720],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* 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),
   },
   {
     /* [269] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[719],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[858],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [270] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[718],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[859],
+    /* return matcher indices */ &kMatcherIndices[40],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [271] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[717],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[871],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [272] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[398],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[872],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [273] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[395],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[869],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [274] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[389],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ true,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[870],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [275] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[383],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ true,
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[439],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [276] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[716],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* 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),
   },
   {
     /* [277] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[715],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[875],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [278] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[499],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[876],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [279] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[501],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[677],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [280] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[753],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[801],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [281] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[754],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[878],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [282] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[713],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[879],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [283] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[712],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[880],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [284] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[711],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[881],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [285] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[710],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[882],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [286] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[751],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[885],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [287] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[752],
-    /* return matcher indices */ &kMatcherIndices[35],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[737],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [288] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[709],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[789],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [289] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[708],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[787],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [290] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[792],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[785],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [291] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[791],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[843],
+    /* return matcher indices */ &kMatcherIndices[200],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [292] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[796],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[868],
+    /* return matcher indices */ &kMatcherIndices[135],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [293] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[795],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[973],
+    /* return matcher indices */ &kMatcherIndices[199],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [294] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[392],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[905],
+    /* return matcher indices */ &kMatcherIndices[117],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [295] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[380],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[841],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [296] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[798],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[842],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [297] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[797],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[561],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [298] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[637],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[567],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [299] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[635],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[873],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [300] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[800],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[874],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [301] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[799],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[838],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [302] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[802],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[839],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [303] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[801],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[836],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [304] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[657],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[837],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [305] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[659],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[834],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [306] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[805],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[835],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [307] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[804],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[773],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [308] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[807],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[771],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [309] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[806],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[956],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [310] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[714],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[955],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [311] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[808],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[495],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [312] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[668],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[498],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [313] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[667],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[831],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [314] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[794],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[833],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [315] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[793],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[954],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [316] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[368],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[953],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [317] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[365],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[944],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [318] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[661],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[943],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [319] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[479],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[829],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [320] */
-    /* num parameters */ 0,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[810],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[830],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [321] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[704],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[942],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [322] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[705],
-    /* return matcher indices */ &kMatcherIndices[113],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[934],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [323] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[706],
-    /* return matcher indices */ &kMatcherIndices[113],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[918],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [324] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[707],
-    /* return matcher indices */ &kMatcherIndices[113],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[917],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [325] */
     /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[740],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* 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),
   },
   {
     /* [326] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[495],
-    /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[915],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [327] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[497],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[549],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [328] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[503],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[552],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [329] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[505],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[555],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline, OverloadFlag::kIsDeprecated),
   },
   {
     /* [330] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[511],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[558],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline, OverloadFlag::kIsDeprecated),
   },
   {
     /* [331] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[513],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[914],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [332] */
-    /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[515],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[913],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [333] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[517],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[745],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [334] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[519],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[743],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [335] */
-    /* num parameters */ 3,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[1],
-    /* open numbers */ &kOpenNumbers[9],
-    /* parameters */ &kParameters[353],
-    /* return matcher indices */ &kMatcherIndices[135],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[826],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [336] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 2,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[5],
-    /* parameters */ &kParameters[678],
-    /* return matcher indices */ &kMatcherIndices[20],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[827],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [337] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[703],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[911],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [338] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[449],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[877],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [339] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[481],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[910],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [340] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[729],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[909],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [341] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[730],
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[755],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [342] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[731],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[757],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [343] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[732],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[907],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [344] */
     /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[733],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[906],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [345] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[734],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[751],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [346] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[625],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[753],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [347] */
-    /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[627],
-    /* return matcher indices */ &kMatcherIndices[18],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[824],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [348] */
-    /* num parameters */ 3,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[362],
-    /* return matcher indices */ &kMatcherIndices[32],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[825],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
   },
   {
     /* [349] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[611],
-    /* return matcher indices */ &kMatcherIndices[121],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[739],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [350] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[595],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[741],
+    /* return matcher indices */ &kMatcherIndices[40],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [351] */
     /* num parameters */ 2,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
-    /* parameters */ &kParameters[597],
-    /* return matcher indices */ &kMatcherIndices[58],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[733],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [352] */
     /* num parameters */ 2,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[0],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[599],
-    /* return matcher indices */ &kMatcherIndices[3],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[735],
+    /* return matcher indices */ &kMatcherIndices[40],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [353] */
-    /* num parameters */ 1,
-    /* num open types */ 0,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[6],
-    /* parameters */ &kParameters[784],
-    /* return matcher indices */ &kMatcherIndices[15],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* 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[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [354] */
-    /* num parameters */ 1,
-    /* num open types */ 1,
-    /* num open numbers */ 1,
-    /* open types */ &kOpenTypes[2],
-    /* open numbers */ &kOpenNumbers[8],
-    /* parameters */ &kParameters[803],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* supported_stages */ PipelineStageSet(PipelineStage::kVertex, PipelineStage::kFragment, PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[729],
+    /* return matcher indices */ &kMatcherIndices[40],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
   {
     /* [355] */
-    /* num parameters */ 0,
-    /* num open types */ 0,
-    /* num open numbers */ 0,
-    /* open types */ &kOpenTypes[5],
-    /* open numbers */ &kOpenNumbers[10],
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[675],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [356] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[683],
+    /* return matcher indices */ &kMatcherIndices[40],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [357] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[812],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+  },
+  {
+    /* [358] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[823],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+  },
+  {
+    /* [359] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[807],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+  },
+  {
+    /* [360] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[808],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+  },
+  {
+    /* [361] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[805],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+  },
+  {
+    /* [362] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[806],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+  },
+  {
+    /* [363] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[803],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+  },
+  {
+    /* [364] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[804],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+  },
+  {
+    /* [365] */
+    /* 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[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [366] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[795],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [367] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[845],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [368] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[844],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [369] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[811],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [370] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[810],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [371] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[814],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [372] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[813],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [373] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[601],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [374] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[599],
+    /* return matcher indices */ &kMatcherIndices[40],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [375] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[816],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [376] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[815],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [377] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[818],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [378] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[817],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [379] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[820],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [380] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[819],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [381] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[822],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [382] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[821],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [383] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[453],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [384] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[450],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [385] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[832],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [386] */
+    /* 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::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [387] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[731],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [388] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[761],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [389] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[639],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [390] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[635],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [391] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[966],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [392] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[952],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [393] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[976],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [394] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[971],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [395] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[978],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [396] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[986],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [397] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[939],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [398] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[949],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [399] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[922],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [400] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[925],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [401] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[19],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[862],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [402] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[19],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[863],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [403] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[860],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [404] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[861],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [405] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[963],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [406] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[900],
+    /* return matcher indices */ &kMatcherIndices[37],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [407] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[747],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [408] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[749],
+    /* return matcher indices */ &kMatcherIndices[40],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [409] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[904],
+    /* return matcher indices */ &kMatcherIndices[103],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [410] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[711],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [411] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[713],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [412] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[715],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [413] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[717],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [414] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[719],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [415] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[721],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [416] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[723],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [417] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[725],
     /* return matcher indices */ nullptr,
-    /* supported_stages */ PipelineStageSet(PipelineStage::kCompute),
-    /* is_deprecated */ false,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [418] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[847],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [419] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[605],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [420] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[603],
+    /* return matcher indices */ &kMatcherIndices[21],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [421] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[967],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [422] */
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [423] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[901],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [424] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[902],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [425] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[903],
+    /* return matcher indices */ &kMatcherIndices[103],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [426] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[709],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [427] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[987],
+    /* return matcher indices */ &kMatcherIndices[103],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [428] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[5],
+    /* parameters */ &kParameters[908],
+    /* return matcher indices */ &kMatcherIndices[23],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [429] */
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [430] */
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[507],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [431] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[767],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [432] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[989],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [433] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[958],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [434] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[972],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [435] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[977],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [436] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[982],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [437] */
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[504],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [438] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[597],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [439] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[799],
+    /* return matcher indices */ &kMatcherIndices[44],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [440] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[8],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[797],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [441] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[809],
+    /* return matcher indices */ &kMatcherIndices[5],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [442] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[20],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[791],
+    /* return matcher indices */ &kMatcherIndices[121],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+  },
+  {
+    /* [443] */
+    /* num parameters */ 1,
+    /* 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),
+  },
+  {
+    /* [444] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[591],
+    /* return matcher indices */ &kMatcherIndices[105],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
   },
 };
 
@@ -10250,336 +12537,336 @@
     /* fn abs<T : fiu32>(T) -> T */
     /* fn abs<N : num, T : fiu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[250],
+    /* overloads */ &kOverloads[405],
   },
   {
     /* [1] */
     /* fn acos(f32) -> f32 */
     /* fn acos<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[310],
+    /* overloads */ &kOverloads[399],
   },
   {
     /* [2] */
     /* fn all(bool) -> bool */
     /* fn all<N : num>(vec<N, bool>) -> bool */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[308],
+    /* overloads */ &kOverloads[397],
   },
   {
     /* [3] */
     /* fn any(bool) -> bool */
     /* fn any<N : num>(vec<N, bool>) -> bool */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[306],
+    /* overloads */ &kOverloads[395],
   },
   {
     /* [4] */
     /* fn arrayLength<T, A : access>(ptr<storage, array<T>, A>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[354],
+    /* overloads */ &kOverloads[443],
   },
   {
     /* [5] */
     /* fn asin(f32) -> f32 */
     /* fn asin<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[302],
+    /* overloads */ &kOverloads[393],
   },
   {
     /* [6] */
     /* fn atan(f32) -> f32 */
     /* fn atan<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[300],
+    /* overloads */ &kOverloads[391],
   },
   {
     /* [7] */
     /* fn atan2(f32, f32) -> f32 */
     /* fn atan2<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[298],
+    /* overloads */ &kOverloads[387],
   },
   {
     /* [8] */
     /* fn ceil(f32) -> f32 */
     /* fn ceil<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[296],
+    /* overloads */ &kOverloads[385],
   },
   {
     /* [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> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[294],
+    /* overloads */ &kOverloads[383],
   },
   {
     /* [10] */
     /* fn cos(f32) -> f32 */
     /* fn cos<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[292],
+    /* overloads */ &kOverloads[381],
   },
   {
     /* [11] */
     /* fn cosh(f32) -> f32 */
     /* fn cosh<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[314],
+    /* overloads */ &kOverloads[379],
   },
   {
     /* [12] */
     /* fn countLeadingZeros<T : iu32>(T) -> T */
     /* fn countLeadingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[290],
+    /* overloads */ &kOverloads[377],
   },
   {
     /* [13] */
     /* fn countOneBits<T : iu32>(T) -> T */
     /* fn countOneBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[258],
+    /* overloads */ &kOverloads[375],
   },
   {
     /* [14] */
     /* fn countTrailingZeros<T : iu32>(T) -> T */
     /* fn countTrailingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[256],
+    /* overloads */ &kOverloads[371],
   },
   {
     /* [15] */
     /* fn cross(vec3<f32>, vec3<f32>) -> vec3<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[349],
+    /* overloads */ &kOverloads[442],
   },
   {
     /* [16] */
     /* fn degrees(f32) -> f32 */
     /* fn degrees<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[246],
+    /* overloads */ &kOverloads[369],
   },
   {
     /* [17] */
     /* fn determinant<N : num>(mat<N, N, f32>) -> f32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[353],
+    /* overloads */ &kOverloads[441],
   },
   {
     /* [18] */
     /* fn distance(f32, f32) -> f32 */
     /* fn distance<N : num>(vec<N, f32>, vec<N, f32>) -> f32 */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[240],
+    /* overloads */ &kOverloads[365],
   },
   {
     /* [19] */
     /* fn dot<N : num, T : fiu32>(vec<N, T>, vec<N, T>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[352],
+    /* overloads */ &kOverloads[440],
   },
   {
     /* [20] */
     /* fn dot4I8Packed(u32, u32) -> i32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[351],
+    /* overloads */ &kOverloads[439],
   },
   {
     /* [21] */
     /* fn dot4U8Packed(u32, u32) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[350],
+    /* overloads */ &kOverloads[438],
   },
   {
     /* [22] */
     /* fn dpdx(f32) -> f32 */
     /* fn dpdx<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[204],
+    /* overloads */ &kOverloads[363],
   },
   {
     /* [23] */
     /* fn dpdxCoarse(f32) -> f32 */
     /* fn dpdxCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[184],
+    /* overloads */ &kOverloads[361],
   },
   {
     /* [24] */
     /* fn dpdxFine(f32) -> f32 */
     /* fn dpdxFine<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[186],
+    /* overloads */ &kOverloads[359],
   },
   {
     /* [25] */
     /* fn dpdy(f32) -> f32 */
     /* fn dpdy<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[188],
+    /* overloads */ &kOverloads[357],
   },
   {
     /* [26] */
     /* fn dpdyCoarse(f32) -> f32 */
     /* fn dpdyCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[190],
+    /* overloads */ &kOverloads[347],
   },
   {
     /* [27] */
     /* fn dpdyFine(f32) -> f32 */
     /* fn dpdyFine<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[192],
+    /* overloads */ &kOverloads[335],
   },
   {
     /* [28] */
     /* fn exp(f32) -> f32 */
     /* fn exp<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[194],
+    /* overloads */ &kOverloads[319],
   },
   {
     /* [29] */
     /* fn exp2(f32) -> f32 */
     /* fn exp2<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[196],
+    /* overloads */ &kOverloads[313],
   },
   {
     /* [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[316],
+    /* overloads */ &kOverloads[311],
   },
   {
     /* [31] */
     /* fn faceForward<N : num>(vec<N, f32>, vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[348],
+    /* overloads */ &kOverloads[437],
   },
   {
     /* [32] */
     /* fn firstLeadingBit<T : iu32>(T) -> T */
     /* fn firstLeadingBit<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[202],
+    /* overloads */ &kOverloads[305],
   },
   {
     /* [33] */
     /* fn firstTrailingBit<T : iu32>(T) -> T */
     /* fn firstTrailingBit<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[182],
+    /* overloads */ &kOverloads[303],
   },
   {
     /* [34] */
     /* fn floor(f32) -> f32 */
     /* fn floor<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[206],
+    /* overloads */ &kOverloads[301],
   },
   {
     /* [35] */
     /* 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[208],
+    /* overloads */ &kOverloads[297],
   },
   {
     /* [36] */
     /* fn fract(f32) -> f32 */
     /* fn fract<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[210],
+    /* overloads */ &kOverloads[295],
   },
   {
     /* [37] */
     /* fn frexp(f32) -> __frexp_result */
     /* fn frexp<N : num>(vec<N, f32>) -> __frexp_result_vec<N> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[212],
+    /* overloads */ &kOverloads[291],
   },
   {
     /* [38] */
     /* fn fwidth(f32) -> f32 */
     /* fn fwidth<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[214],
+    /* overloads */ &kOverloads[273],
   },
   {
     /* [39] */
     /* fn fwidthCoarse(f32) -> f32 */
     /* fn fwidthCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[216],
+    /* overloads */ &kOverloads[271],
   },
   {
     /* [40] */
     /* fn fwidthFine(f32) -> f32 */
     /* fn fwidthFine<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[218],
+    /* overloads */ &kOverloads[299],
   },
   {
     /* [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[220],
+    /* overloads */ &kOverloads[275],
   },
   {
     /* [42] */
     /* fn inverseSqrt(f32) -> f32 */
     /* fn inverseSqrt<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[222],
+    /* 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[224],
+    /* overloads */ &kOverloads[279],
   },
   {
     /* [44] */
     /* fn length(f32) -> f32 */
     /* fn length<N : num>(vec<N, f32>) -> f32 */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[248],
+    /* overloads */ &kOverloads[281],
   },
   {
     /* [45] */
     /* fn log(f32) -> f32 */
     /* fn log<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[228],
+    /* overloads */ &kOverloads[283],
   },
   {
     /* [46] */
     /* fn log2(f32) -> f32 */
     /* fn log2<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[230],
+    /* 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[232],
+    /* 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[264],
+    /* overloads */ &kOverloads[289],
   },
   {
     /* [49] */
@@ -10587,90 +12874,90 @@
     /* 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[176],
+    /* 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[238],
+    /* overloads */ &kOverloads[293],
   },
   {
     /* [51] */
     /* fn normalize<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[345],
+    /* overloads */ &kOverloads[436],
   },
   {
     /* [52] */
     /* fn pack2x16float(vec2<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[344],
+    /* overloads */ &kOverloads[435],
   },
   {
     /* [53] */
     /* fn pack2x16snorm(vec2<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[343],
+    /* overloads */ &kOverloads[434],
   },
   {
     /* [54] */
     /* fn pack2x16unorm(vec2<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[342],
+    /* overloads */ &kOverloads[421],
   },
   {
     /* [55] */
     /* fn pack4x8snorm(vec4<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[341],
+    /* overloads */ &kOverloads[433],
   },
   {
     /* [56] */
     /* fn pack4x8unorm(vec4<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[340],
+    /* overloads */ &kOverloads[432],
   },
   {
     /* [57] */
     /* fn pow(f32, f32) -> f32 */
     /* fn pow<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[252],
+    /* overloads */ &kOverloads[307],
   },
   {
     /* [58] */
     /* fn radians(f32) -> f32 */
     /* fn radians<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[254],
+    /* overloads */ &kOverloads[309],
   },
   {
     /* [59] */
     /* fn reflect<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[339],
+    /* overloads */ &kOverloads[431],
   },
   {
     /* [60] */
     /* fn refract<N : num>(vec<N, f32>, vec<N, f32>, f32) -> vec<N, f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[338],
+    /* overloads */ &kOverloads[430],
   },
   {
     /* [61] */
     /* fn reverseBits<T : iu32>(T) -> T */
     /* fn reverseBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[260],
+    /* overloads */ &kOverloads[315],
   },
   {
     /* [62] */
     /* fn round(f32) -> f32 */
     /* fn round<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[262],
+    /* overloads */ &kOverloads[317],
   },
   {
     /* [63] */
@@ -10678,125 +12965,125 @@
     /* 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[179],
+    /* overloads */ &kOverloads[257],
   },
   {
     /* [64] */
     /* fn sign(f32) -> f32 */
     /* fn sign<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[266],
+    /* overloads */ &kOverloads[321],
   },
   {
     /* [65] */
     /* fn sin(f32) -> f32 */
     /* fn sin<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[268],
+    /* overloads */ &kOverloads[323],
   },
   {
     /* [66] */
     /* fn sinh(f32) -> f32 */
     /* fn sinh<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[270],
+    /* 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[272],
+    /* overloads */ &kOverloads[327],
   },
   {
     /* [68] */
     /* 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[274],
+    /* overloads */ &kOverloads[329],
   },
   {
     /* [69] */
     /* fn sqrt(f32) -> f32 */
     /* fn sqrt<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[276],
+    /* overloads */ &kOverloads[331],
   },
   {
     /* [70] */
     /* fn step(f32, f32) -> f32 */
     /* fn step<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[278],
+    /* overloads */ &kOverloads[333],
   },
   {
     /* [71] */
     /* fn storageBarrier() */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[320],
+    /* overloads */ &kOverloads[429],
   },
   {
     /* [72] */
     /* fn tan(f32) -> f32 */
     /* fn tan<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[282],
+    /* overloads */ &kOverloads[337],
   },
   {
     /* [73] */
     /* fn tanh(f32) -> f32 */
     /* fn tanh<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[284],
+    /* overloads */ &kOverloads[339],
   },
   {
     /* [74] */
     /* fn transpose<M : num, N : num>(mat<M, N, f32>) -> mat<N, M, f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[336],
+    /* overloads */ &kOverloads[428],
   },
   {
     /* [75] */
     /* fn trunc(f32) -> f32 */
     /* fn trunc<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[288],
+    /* overloads */ &kOverloads[343],
   },
   {
     /* [76] */
     /* fn unpack2x16float(u32) -> vec2<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[324],
+    /* overloads */ &kOverloads[427],
   },
   {
     /* [77] */
     /* fn unpack2x16snorm(u32) -> vec2<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[323],
+    /* overloads */ &kOverloads[409],
   },
   {
     /* [78] */
     /* fn unpack2x16unorm(u32) -> vec2<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[322],
+    /* overloads */ &kOverloads[425],
   },
   {
     /* [79] */
     /* fn unpack4x8snorm(u32) -> vec4<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[321],
+    /* overloads */ &kOverloads[424],
   },
   {
     /* [80] */
     /* fn unpack4x8unorm(u32) -> vec4<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[337],
+    /* overloads */ &kOverloads[423],
   },
   {
     /* [81] */
     /* fn workgroupBarrier() */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[355],
+    /* overloads */ &kOverloads[422],
   },
   {
     /* [82] */
@@ -10845,7 +13132,7 @@
     /* 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[57],
+    /* overloads */ &kOverloads[71],
   },
   {
     /* [84] */
@@ -10856,7 +13143,7 @@
     /* 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[144],
+    /* overloads */ &kOverloads[157],
   },
   {
     /* [85] */
@@ -10866,7 +13153,7 @@
     /* 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[155],
+    /* overloads */ &kOverloads[205],
   },
   {
     /* [86] */
@@ -10881,14 +13168,14 @@
     /* fn textureNumLevels(texture: texture_depth_cube) -> i32 */
     /* fn textureNumLevels(texture: texture_depth_cube_array) -> i32 */
     /* num overloads */ 10,
-    /* overloads */ &kOverloads[81],
+    /* overloads */ &kOverloads[105],
   },
   {
     /* [87] */
     /* fn textureNumSamples<T : fiu32>(texture: texture_multisampled_2d<T>) -> i32 */
     /* fn textureNumSamples(texture: texture_depth_multisampled_2d) -> i32 */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[312],
+    /* overloads */ &kOverloads[367],
   },
   {
     /* [88] */
@@ -10908,7 +13195,7 @@
     /* 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[42],
+    /* overloads */ &kOverloads[27],
   },
   {
     /* [89] */
@@ -10921,7 +13208,7 @@
     /* 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[117],
+    /* overloads */ &kOverloads[133],
   },
   {
     /* [90] */
@@ -10932,7 +13219,7 @@
     /* 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[138],
+    /* overloads */ &kOverloads[169],
   },
   {
     /* [91] */
@@ -10943,7 +13230,7 @@
     /* 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[132],
+    /* overloads */ &kOverloads[163],
   },
   {
     /* [92] */
@@ -10956,7 +13243,7 @@
     /* 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[109],
+    /* overloads */ &kOverloads[141],
   },
   {
     /* [93] */
@@ -10976,7 +13263,7 @@
     /* 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[27],
+    /* overloads */ &kOverloads[42],
   },
   {
     /* [94] */
@@ -10993,7 +13280,7 @@
     /* 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[69],
+    /* overloads */ &kOverloads[83],
   },
   {
     /* [95] */
@@ -11007,115 +13294,126 @@
     /* 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[100],
+    /* overloads */ &kOverloads[115],
   },
   {
     /* [96] */
     /* fn atomicLoad<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[325],
+    /* overloads */ &kOverloads[418],
   },
   {
     /* [97] */
     /* fn atomicStore<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[326],
+    /* overloads */ &kOverloads[417],
   },
   {
     /* [98] */
     /* fn atomicAdd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[327],
+    /* overloads */ &kOverloads[416],
   },
   {
     /* [99] */
     /* fn atomicSub<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[328],
+    /* overloads */ &kOverloads[415],
   },
   {
     /* [100] */
     /* fn atomicMax<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[329],
+    /* overloads */ &kOverloads[414],
   },
   {
     /* [101] */
     /* fn atomicMin<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[330],
+    /* overloads */ &kOverloads[413],
   },
   {
     /* [102] */
     /* fn atomicAnd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[331],
+    /* overloads */ &kOverloads[412],
   },
   {
     /* [103] */
     /* fn atomicOr<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[332],
+    /* overloads */ &kOverloads[411],
   },
   {
     /* [104] */
     /* fn atomicXor<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[333],
+    /* overloads */ &kOverloads[410],
   },
   {
     /* [105] */
     /* fn atomicExchange<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[334],
+    /* overloads */ &kOverloads[426],
   },
   {
     /* [106] */
     /* fn atomicCompareExchangeWeak<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T, T) -> vec2<T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[335],
+    /* overloads */ &kOverloads[444],
   },
 };
 
-constexpr IntrinsicInfo kOperators[] = {
+constexpr IntrinsicInfo kUnaryOperators[] = {
   {
     /* [0] */
     /* op !(bool) -> bool */
     /* op !<N : num>(vec<N, bool>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[286],
+    /* overloads */ &kOverloads[269],
   },
   {
     /* [1] */
     /* op ~<T : iu32>(T) -> T */
     /* op ~<T : iu32, N : num>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[280],
+    /* overloads */ &kOverloads[403],
   },
   {
     /* [2] */
     /* op -<T : fi32>(T) -> T */
     /* op -<T : fi32, N : num>(vec<N, T>) -> vec<N, T> */
-    /* op -<T : fiu32>(T, T) -> T */
-    /* op -<T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
-    /* op -<T : fiu32, N : num>(vec<N, T>, T) -> vec<N, T> */
-    /* 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 */ 7,
-    /* overloads */ &kOverloads[125],
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[401],
   },
+};
+constexpr uint8_t kUnaryOperatorNot = 0;
+constexpr uint8_t kUnaryOperatorComplement = 1;
+constexpr uint8_t kUnaryOperatorMinus = 2;
+
+constexpr IntrinsicInfo kBinaryOperators[] = {
   {
-    /* [3] */
+    /* [0] */
     /* op +<T : fiu32>(T, T) -> T */
     /* op +<T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* op +<T : fiu32, N : num>(vec<N, T>, T) -> vec<N, T> */
     /* 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[150],
+    /* overloads */ &kOverloads[220],
   },
   {
-    /* [4] */
+    /* [1] */
+    /* op -<T : fiu32>(T, T) -> T */
+    /* op -<T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
+    /* op -<T : fiu32, N : num>(vec<N, T>, T) -> vec<N, T> */
+    /* 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],
+  },
+  {
+    /* [2] */
     /* op *<T : fiu32>(T, T) -> T */
     /* op *<T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* op *<T : fiu32, N : num>(vec<N, T>, T) -> vec<N, T> */
@@ -11126,139 +13424,309 @@
     /* 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[91],
+    /* overloads */ &kOverloads[124],
   },
   {
-    /* [5] */
+    /* [3] */
     /* op /<T : fiu32>(T, T) -> T */
     /* op /<T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* 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[172],
+    /* overloads */ &kOverloads[243],
   },
   {
-    /* [6] */
+    /* [4] */
     /* op %<T : fiu32>(T, T) -> T */
     /* op %<T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* 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[160],
+    /* overloads */ &kOverloads[239],
   },
   {
-    /* [7] */
+    /* [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[244],
+    /* overloads */ &kOverloads[389],
   },
   {
-    /* [8] */
+    /* [6] */
     /* op &(bool, bool) -> bool */
     /* op &<N : num>(vec<N, bool>, vec<N, bool>) -> vec<N, bool> */
     /* 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[164],
+    /* overloads */ &kOverloads[235],
   },
   {
-    /* [9] */
+    /* [7] */
     /* op |(bool, bool) -> bool */
     /* op |<N : num>(vec<N, bool>, vec<N, bool>) -> vec<N, bool> */
     /* 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[168],
+    /* overloads */ &kOverloads[247],
+  },
+  {
+    /* [8] */
+    /* op &&(bool, bool) -> bool */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[419],
+  },
+  {
+    /* [9] */
+    /* op ||(bool, bool) -> bool */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[420],
   },
   {
     /* [10] */
-    /* op &&(bool, bool) -> bool */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[346],
-  },
-  {
-    /* [11] */
-    /* op ||(bool, bool) -> bool */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[347],
-  },
-  {
-    /* [12] */
     /* 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[200],
+    /* overloads */ &kOverloads[373],
   },
   {
-    /* [13] */
+    /* [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[198],
+    /* overloads */ &kOverloads[355],
   },
   {
-    /* [14] */
+    /* [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[226],
+    /* overloads */ &kOverloads[353],
   },
   {
-    /* [15] */
+    /* [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[234],
+    /* overloads */ &kOverloads[351],
   },
   {
-    /* [16] */
+    /* [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[236],
+    /* overloads */ &kOverloads[349],
   },
   {
-    /* [17] */
+    /* [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[242],
+    /* overloads */ &kOverloads[407],
   },
   {
-    /* [18] */
+    /* [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[304],
+    /* overloads */ &kOverloads[345],
   },
   {
-    /* [19] */
+    /* [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[318],
+    /* overloads */ &kOverloads[341],
   },
 };
-constexpr uint8_t kOperatorNot = 0;
-constexpr uint8_t kOperatorComplement = 1;
-constexpr uint8_t kOperatorMinus = 2;
-constexpr uint8_t kOperatorPlus = 3;
-constexpr uint8_t kOperatorStar = 4;
-constexpr uint8_t kOperatorDivide = 5;
-constexpr uint8_t kOperatorModulo = 6;
-constexpr uint8_t kOperatorXor = 7;
-constexpr uint8_t kOperatorAnd = 8;
-constexpr uint8_t kOperatorOr = 9;
-constexpr uint8_t kOperatorLogicalAnd = 10;
-constexpr uint8_t kOperatorLogicalOr = 11;
-constexpr uint8_t kOperatorEqual = 12;
-constexpr uint8_t kOperatorNotEqual = 13;
-constexpr uint8_t kOperatorLessThan = 14;
-constexpr uint8_t kOperatorGreaterThan = 15;
-constexpr uint8_t kOperatorLessThanEqual = 16;
-constexpr uint8_t kOperatorGreaterThanEqual = 17;
-constexpr uint8_t kOperatorShiftLeft = 18;
-constexpr uint8_t kOperatorShiftRight = 19;
+constexpr uint8_t kBinaryOperatorPlus = 0;
+constexpr uint8_t kBinaryOperatorMinus = 1;
+constexpr uint8_t kBinaryOperatorStar = 2;
+constexpr uint8_t kBinaryOperatorDivide = 3;
+constexpr uint8_t kBinaryOperatorModulo = 4;
+constexpr uint8_t kBinaryOperatorXor = 5;
+constexpr uint8_t kBinaryOperatorAnd = 6;
+constexpr uint8_t kBinaryOperatorOr = 7;
+constexpr uint8_t kBinaryOperatorLogicalAnd = 8;
+constexpr uint8_t kBinaryOperatorLogicalOr = 9;
+constexpr uint8_t kBinaryOperatorEqual = 10;
+constexpr uint8_t kBinaryOperatorNotEqual = 11;
+constexpr uint8_t kBinaryOperatorLessThan = 12;
+constexpr uint8_t kBinaryOperatorGreaterThan = 13;
+constexpr uint8_t kBinaryOperatorLessThanEqual = 14;
+constexpr uint8_t kBinaryOperatorGreaterThanEqual = 15;
+constexpr uint8_t kBinaryOperatorShiftLeft = 16;
+constexpr uint8_t kBinaryOperatorShiftRight = 17;
+
+constexpr IntrinsicInfo kConstructorsAndConverters[] = {
+  {
+    /* [0] */
+    /* ctor i32() -> i32 */
+    /* ctor i32(i32) -> i32 */
+    /* conv i32<T : scalar_no_i32>(T) -> i32 */
+    /* num overloads */ 3,
+    /* overloads */ &kOverloads[251],
+  },
+  {
+    /* [1] */
+    /* ctor u32() -> u32 */
+    /* ctor u32(u32) -> u32 */
+    /* conv u32<T : scalar_no_u32>(T) -> u32 */
+    /* num overloads */ 3,
+    /* overloads */ &kOverloads[260],
+  },
+  {
+    /* [2] */
+    /* ctor f32() -> f32 */
+    /* ctor f32(f32) -> f32 */
+    /* conv f32<T : scalar_no_f32>(T) -> f32 */
+    /* num overloads */ 3,
+    /* overloads */ &kOverloads[263],
+  },
+  {
+    /* [3] */
+    /* ctor bool() -> bool */
+    /* ctor bool(bool) -> bool */
+    /* conv bool<T : scalar_no_bool>(T) -> bool */
+    /* num overloads */ 3,
+    /* overloads */ &kOverloads[254],
+  },
+  {
+    /* [4] */
+    /* 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 : 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],
+  },
+  {
+    /* [5] */
+    /* ctor vec3<T : scalar>() -> vec3<T> */
+    /* ctor vec3<T : scalar>(vec3<T>) -> vec3<T> */
+    /* ctor vec3<T : abstract_or_scalar>(T) -> vec3<T> */
+    /* ctor vec3<T : abstract_or_scalar>(x: T, y: T, z: T) -> vec3<T> */
+    /* 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 : 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],
+  },
+  {
+    /* [6] */
+    /* ctor vec4<T : scalar>() -> vec4<T> */
+    /* ctor vec4<T : scalar>(vec4<T>) -> vec4<T> */
+    /* ctor vec4<T : abstract_or_scalar>(T) -> vec4<T> */
+    /* ctor vec4<T : abstract_or_scalar>(x: T, y: T, z: T, w: T) -> vec4<T> */
+    /* ctor vec4<T : abstract_or_scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T> */
+    /* ctor vec4<T : abstract_or_scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T> */
+    /* ctor vec4<T : abstract_or_scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T> */
+    /* ctor vec4<T : abstract_or_scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T> */
+    /* 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 : 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],
+  },
+  {
+    /* [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[175],
+  },
+  {
+    /* [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[200],
+  },
+  {
+    /* [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[195],
+  },
+  {
+    /* [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,
+    /* overloads */ &kOverloads[230],
+  },
+  {
+    /* [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[180],
+  },
+  {
+    /* [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[185],
+  },
+  {
+    /* [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[210],
+  },
+  {
+    /* [15] */
+    /* ctor mat4x4() -> mat4x4<f32> */
+    /* ctor mat4x4<f32>(mat4x4<f32>) -> mat4x4<f32> */
+    /* 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[225],
+  },
+};
 
 // clang-format on
diff --git a/src/tint/resolver/intrinsic_table.inl.tmpl b/src/tint/resolver/intrinsic_table.inl.tmpl
index 9eeaebc..f5e3575 100644
--- a/src/tint/resolver/intrinsic_table.inl.tmpl
+++ b/src/tint/resolver/intrinsic_table.inl.tmpl
@@ -51,8 +51,8 @@
 {{- end }}
 };
 
-constexpr OpenTypeInfo kOpenTypes[] = {
-{{- range $i, $o := .OpenTypes }}
+constexpr TemplateTypeInfo kTemplateTypes[] = {
+{{- range $i, $o := .TemplateTypes }}
   {
     /* [{{$i}}] */
     /* name */ "{{$o.Name}}",
@@ -64,8 +64,8 @@
 {{- end }}
 };
 
-constexpr OpenNumberInfo kOpenNumbers[] = {
-{{- range $i, $o := .OpenNumbers }}
+constexpr TemplateNumberInfo kTemplateNumbers[] = {
+{{- range $i, $o := .TemplateNumbers }}
   {
     /* [{{$i}}] */
     /* name */ "{{$o.Name}}",
@@ -82,14 +82,14 @@
   {
     /* [{{$i}}] */
     /* num parameters */ {{$o.NumParameters}},
-    /* num open types */ {{$o.NumOpenTypes}},
-    /* num open numbers */ {{$o.NumOpenNumbers}},
-    /* open types */
-{{-   if $o.OpenTypesOffset }} &kOpenTypes[{{$o.OpenTypesOffset}}],
+    /* num template types */ {{$o.NumTemplateTypes}},
+    /* num template numbers */ {{$o.NumTemplateNumbers}},
+    /* template types */
+{{-   if $o.TemplateTypesOffset }} &kTemplateTypes[{{$o.TemplateTypesOffset}}],
 {{-   else                  }} nullptr,
 {{-   end }}
-    /* open numbers */
-{{-   if $o.OpenNumbersOffset }} &kOpenNumbers[{{$o.OpenNumbersOffset}}]
+    /* template numbers */
+{{-   if $o.TemplateNumbersOffset }} &kTemplateNumbers[{{$o.TemplateNumbersOffset}}]
 {{-   else                    }} nullptr
 {{-   end }},
     /* parameters */ &kParameters[{{$o.ParametersOffset}}],
@@ -97,11 +97,11 @@
 {{-   if $o.ReturnMatcherIndicesOffset }} &kMatcherIndices[{{$o.ReturnMatcherIndicesOffset}}]
 {{-   else                             }} nullptr
 {{-   end }},
-    /* supported_stages */ PipelineStageSet(
+    /* flags */ OverloadFlags(OverloadFlag::kIs{{Title $o.Kind}}
 {{-   range $i, $u := $o.CanBeUsedInStage.List -}}
-{{-     if $i -}}, {{end}}PipelineStage::k{{Title $u}}
-{{-   end }}),
-    /* is_deprecated */ {{$o.IsDeprecated}},
+        , OverloadFlag::kSupports{{Title $u}}Pipeline
+{{-   end }}
+{{-   if $o.IsDeprecated}}, OverloadFlag::kIsDeprecated{{end }}),
   },
 {{- end }}
 };
@@ -119,8 +119,8 @@
 {{- end }}
 };
 
-constexpr IntrinsicInfo kOperators[] = {
-{{- range $i, $o := .Operators }}
+constexpr IntrinsicInfo kUnaryOperators[] = {
+{{- range $i, $o := .UnaryOperators }}
   {
     /* [{{$i}}] */
 {{-   range $o.OverloadDescriptions }}
@@ -132,10 +132,40 @@
 {{- end }}
 };
 
-{{- range $i, $o := .Operators }}
-constexpr uint8_t kOperator{{template "OperatorName" $o.Name}} = {{$i}};
+{{- range $i, $o := .UnaryOperators }}
+constexpr uint8_t kUnaryOperator{{template "OperatorName" $o.Name}} = {{$i}};
 {{- end }}
 
+constexpr IntrinsicInfo kBinaryOperators[] = {
+{{- range $i, $o := .BinaryOperators }}
+  {
+    /* [{{$i}}] */
+{{-   range $o.OverloadDescriptions }}
+    /* {{.}} */
+{{-   end }}
+    /* num overloads */ {{$o.NumOverloads}},
+    /* overloads */ &kOverloads[{{$o.OverloadsOffset}}],
+  },
+{{- end }}
+};
+
+{{- range $i, $o := .BinaryOperators }}
+constexpr uint8_t kBinaryOperator{{template "OperatorName" $o.Name}} = {{$i}};
+{{- end }}
+
+constexpr IntrinsicInfo kConstructorsAndConverters[] = {
+{{- range $i, $o := .ConstructorsAndConverters }}
+  {
+    /* [{{$i}}] */
+{{-   range $o.OverloadDescriptions }}
+    /* {{.}} */
+{{-   end }}
+    /* num overloads */ {{$o.NumOverloads}},
+    /* overloads */ &kOverloads[{{$o.OverloadsOffset}}],
+  },
+{{- end }}
+};
+
 // clang-format on
 {{ end -}}
 
@@ -150,7 +180,7 @@
 class {{$class}} : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -158,7 +188,7 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* {{$class}}::Match(MatchState& state, const sem::Type* ty) const {
@@ -177,7 +207,7 @@
   return build_{{TrimLeft .Name "_"}}(state{{range .TemplateParams}}, {{.GetName}}{{end}});
 }
 
-std::string {{$class}}::String(MatchState&{{if .TemplateParams}} state{{end}}) const {
+std::string {{$class}}::String(MatchState*{{if .TemplateParams}} state{{end}}) const {
 {{- range .TemplateParams }}
 {{-   template "DeclareLocalTemplateParamName" . }}
 {{- end  }}
@@ -206,7 +236,7 @@
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
   /// expected, canonicalized type on success.
-  /// Match may close open types and numbers in state.
+  /// 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
@@ -214,11 +244,11 @@
                          const sem::Type* type) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 const sem::Type* {{$class}}::Match(MatchState& state, const sem::Type* ty) const {
-{{- range .Types }}
+{{- range .PrecedenceSortedTypes }}
   if (match_{{.Name}}(ty)) {
     return build_{{.Name}}(state);
   }
@@ -226,15 +256,18 @@
   return nullptr;
 }
 
-std::string {{$class}}::String(MatchState&) const {
-  return "
+std::string {{$class}}::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
 {{- range .Types -}}
-{{-   if      IsFirstIn . $.Types }}{{.Name}}
-{{-   else if IsLastIn  . $.Types }} or {{.Name}}
-{{-   else                        }}, {{.Name}}
+{{-   if      IsFirstIn . $.Types }} << {{PascalCase .Name}}().String(nullptr)
+{{-   else if IsLastIn  . $.Types }} << " or " << {{PascalCase .Name}}().String(nullptr)
+{{-   else                        }} << ", " << {{PascalCase .Name}}().String(nullptr)
 {{-   end -}}
-{{- end -}}
-  ";
+{{- end -}};
+  return ss.str();
 }
 {{  end -}}
 
@@ -250,14 +283,14 @@
 class {{$class}} : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
-  /// Match may close open types and numbers in state.
+  /// Match may define template numbers in state.
   /// @param state the MatchState
   /// @param number the enum value as a Number
   /// @return true if the enum value matches the set
   Number Match(MatchState& state, Number number) const override;
   /// @param state the MatchState
   /// @return a string representation of the matcher.
-  std::string String(MatchState& state) const override;
+  std::string String(MatchState* state) const override;
 };
 
 {{ if eq 1 (len .Options) -}}
@@ -282,7 +315,7 @@
 }
 {{- end }}
 
-std::string {{$class}}::String(MatchState&) const {
+std::string {{$class}}::String(MatchState*) const {
   return "
 {{- range .Options -}}
 {{-   if      IsFirstIn . $.Options }}{{.Name}}
@@ -302,15 +335,15 @@
  private:
 {{- $t_names := Map -}}
 {{- $n_names := Map -}}
-{{- range Iterate .Sem.MaxOpenTypes -}}
-{{-   $name := printf "open_type_%v" . -}}
+{{- range Iterate .Sem.MaxTemplateTypes -}}
+{{-   $name := printf "template_type_%v" . -}}
 {{-   $t_names.Put . $name }}
-  OpenTypeMatcher {{$name}}_{ {{- . -}} };
+  TemplateTypeMatcher {{$name}}_{ {{- . -}} };
 {{- end }}
-{{- range Iterate .Sem.MaxOpenNumbers -}}
-{{-   $name := printf "open_number_%v" . -}}
+{{- range Iterate .Sem.MaxTemplateNumbers -}}
+{{-   $name := printf "template_number_%v" . -}}
 {{-   $n_names.Put . $name }}
-  OpenNumberMatcher {{$name}}_{ {{- . -}} };
+  TemplateNumberMatcher {{$name}}_{ {{- . -}} };
 {{- end }}
 {{- range .Sem.Types -}}
 {{-   $name := PascalCase .Name -}}
@@ -334,7 +367,7 @@
   /// Destructor
   ~Matchers();
 
-  /// The open-types, types, and type matchers
+  /// The template types, types, and type matchers
   TypeMatcher const* const type[{{len .TMatchers}}] = {
 {{- range $i, $m := .TMatchers }}
     /* [{{$i}}] */
@@ -344,7 +377,7 @@
 {{- end }}
   };
 
-  /// The open-numbers, and number matchers
+  /// The template numbers, and number matchers
   NumberMatcher const* const number[{{len .NMatchers}}] = {
 {{- range $i, $m := .NMatchers }}
     /* [{{$i}}] */
@@ -375,11 +408,11 @@
 {{-                   define "DeclareLocalTemplateParamName"                 -}}
 {{- /* ------------------------------------------------------------------ */ -}}
 {{-   if      IsTemplateTypeParam . }}
-  const std::string {{.Name}} = state.TypeName();
+  const std::string {{.Name}} = state->TypeName();
 {{-   else if IsTemplateNumberParam . }}
-  const std::string {{.Name}} = state.NumName();
+  const std::string {{.Name}} = state->NumName();
 {{-   else if IsTemplateEnumParam . }}
-  const std::string {{.Name}} = state.NumName();
+  const std::string {{.Name}} = state->NumName();
 {{-   end -}}
 {{- end -}}
 
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index 79d1a53..4a5c171 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -14,8 +14,11 @@
 
 #include "src/tint/resolver/intrinsic_table.h"
 
+#include <utility>
+
 #include "gmock/gmock.h"
 #include "src/tint/program_builder.h"
+#include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/atomic.h"
 #include "src/tint/sem/depth_multisampled_texture.h"
 #include "src/tint/sem/depth_texture.h"
@@ -24,8 +27,11 @@
 #include "src/tint/sem/reference.h"
 #include "src/tint/sem/sampled_texture.h"
 #include "src/tint/sem/storage_texture.h"
+#include "src/tint/sem/test_helper.h"
+#include "src/tint/sem/type_constructor.h"
+#include "src/tint/sem/type_conversion.h"
 
-namespace tint {
+namespace tint::resolver {
 namespace {
 
 using ::testing::HasSubstr;
@@ -34,6 +40,12 @@
 using Parameter = sem::Parameter;
 using ParameterUsage = sem::ParameterUsage;
 
+using AFloatV = builder::vec<3, AFloat>;
+using AIntV = builder::vec<3, AInt>;
+using f32V = builder::vec<3, f32>;
+using i32V = builder::vec<3, i32>;
+using u32V = builder::vec<3, u32>;
+
 class IntrinsicTableTest : public testing::Test, public ProgramBuilder {
   public:
     std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
@@ -414,7 +426,7 @@
     EXPECT_EQ(result->Parameters()[0]->Type(), f32);
 }
 
-TEST_F(IntrinsicTableTest, MatchOpenType) {
+TEST_F(IntrinsicTableTest, MatchTemplateType) {
     auto* f32 = create<sem::F32>();
     auto* result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
     ASSERT_NE(result, nullptr) << Diagnostics().str();
@@ -426,7 +438,7 @@
     EXPECT_EQ(result->Parameters()[2]->Type(), f32);
 }
 
-TEST_F(IntrinsicTableTest, MismatchOpenType) {
+TEST_F(IntrinsicTableTest, MismatchTemplateType) {
     auto* f32 = create<sem::F32>();
     auto* u32 = create<sem::U32>();
     auto* result = table->Lookup(BuiltinType::kClamp, {f32, u32, f32}, Source{});
@@ -661,5 +673,569 @@
 )");
 }
 
+TEST_F(IntrinsicTableTest, MatchTypeConstructorImplicit) {
+    auto* i32 = create<sem::I32>();
+    auto* vec3_i32 = create<sem::Vector>(i32, 3u);
+    auto* result =
+        table->Lookup(CtorConvIntrinsic::kVec3, nullptr, {i32, i32, i32}, Source{{12, 34}});
+    ASSERT_NE(result, nullptr);
+    EXPECT_EQ(result->ReturnType(), vec3_i32);
+    EXPECT_TRUE(result->Is<sem::TypeConstructor>());
+    ASSERT_EQ(result->Parameters().size(), 3u);
+    EXPECT_EQ(result->Parameters()[0]->Type(), i32);
+    EXPECT_EQ(result->Parameters()[1]->Type(), i32);
+    EXPECT_EQ(result->Parameters()[2]->Type(), i32);
+}
+
+TEST_F(IntrinsicTableTest, MatchTypeConstructorExplicit) {
+    auto* i32 = create<sem::I32>();
+    auto* vec3_i32 = create<sem::Vector>(i32, 3u);
+    auto* result = table->Lookup(CtorConvIntrinsic::kVec3, i32, {i32, i32, i32}, Source{{12, 34}});
+    ASSERT_NE(result, nullptr);
+    EXPECT_EQ(result->ReturnType(), vec3_i32);
+    EXPECT_TRUE(result->Is<sem::TypeConstructor>());
+    ASSERT_EQ(result->Parameters().size(), 3u);
+    EXPECT_EQ(result->Parameters()[0]->Type(), i32);
+    EXPECT_EQ(result->Parameters()[1]->Type(), i32);
+    EXPECT_EQ(result->Parameters()[2]->Type(), i32);
+}
+
+TEST_F(IntrinsicTableTest, MismatchTypeConstructorImplicit) {
+    auto* i32 = create<sem::I32>();
+    auto* f32 = create<sem::F32>();
+    auto* result =
+        table->Lookup(CtorConvIntrinsic::kVec3, nullptr, {i32, f32, i32}, Source{{12, 34}});
+    ASSERT_EQ(result, nullptr);
+    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
+
+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
+)");
+}
+
+TEST_F(IntrinsicTableTest, MismatchTypeConstructorExplicit) {
+    auto* i32 = create<sem::I32>();
+    auto* f32 = create<sem::F32>();
+    auto* result = table->Lookup(CtorConvIntrinsic::kVec3, i32, {i32, f32, i32}, Source{{12, 34}});
+    ASSERT_EQ(result, nullptr);
+    EXPECT_EQ(Diagnostics().str(),
+              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
+
+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
+)");
+}
+
+TEST_F(IntrinsicTableTest, MatchTypeConversion) {
+    auto* i32 = create<sem::I32>();
+    auto* vec3_i32 = create<sem::Vector>(i32, 3u);
+    auto* f32 = create<sem::F32>();
+    auto* vec3_f32 = create<sem::Vector>(f32, 3u);
+    auto* result = table->Lookup(CtorConvIntrinsic::kVec3, i32, {vec3_f32}, Source{{12, 34}});
+    ASSERT_NE(result, nullptr);
+    EXPECT_EQ(result->ReturnType(), vec3_i32);
+    EXPECT_TRUE(result->Is<sem::TypeConversion>());
+    ASSERT_EQ(result->Parameters().size(), 1u);
+    EXPECT_EQ(result->Parameters()[0]->Type(), vec3_f32);
+}
+
+TEST_F(IntrinsicTableTest, MismatchTypeConversion) {
+    auto* arr = create<sem::Array>(create<sem::U32>(), 0u, 4u, 4u, 4u, 4u);
+    auto* f32 = create<sem::F32>();
+    auto* result = table->Lookup(CtorConvIntrinsic::kVec3, f32, {arr}, Source{{12, 34}});
+    ASSERT_EQ(result, nullptr);
+    EXPECT_EQ(Diagnostics().str(),
+              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
+
+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
+)");
+}
+
+TEST_F(IntrinsicTableTest, Err257Arguments) {  // crbug.com/1323605
+    auto* f32 = create<sem::F32>();
+    std::vector<const sem::Type*> arg_tys(257, f32);
+    auto* result = table->Lookup(BuiltinType::kAbs, std::move(arg_tys), Source{});
+    ASSERT_EQ(result, nullptr);
+    ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AbstractBinaryTests
+////////////////////////////////////////////////////////////////////////////////
+namespace AbstractBinaryTests {
+
+struct Case {
+    template <typename RESULT,
+              typename PARAM_LHS,
+              typename PARAM_RHS,
+              typename ARG_LHS,
+              typename ARG_RHS>
+    static Case Create(bool match = true) {
+        return {
+            match,                              //
+            builder::DataType<RESULT>::Sem,     //
+            builder::DataType<PARAM_LHS>::Sem,  //
+            builder::DataType<PARAM_RHS>::Sem,  //
+            builder::DataType<ARG_LHS>::Sem,    //
+            builder::DataType<ARG_RHS>::Sem,    //
+        };
+    }
+    bool expected_match;
+    builder::sem_type_func_ptr expected_result;
+    builder::sem_type_func_ptr expected_param_lhs;
+    builder::sem_type_func_ptr expected_param_rhs;
+    builder::sem_type_func_ptr arg_lhs;
+    builder::sem_type_func_ptr arg_rhs;
+};
+
+struct IntrinsicTableAbstractBinaryTest : public ResolverTestWithParam<Case> {
+    std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
+};
+
+TEST_P(IntrinsicTableAbstractBinaryTest, MatchAdd) {
+    auto* arg_lhs = GetParam().arg_lhs(*this);
+    auto* arg_rhs = GetParam().arg_rhs(*this);
+    auto result = table->Lookup(ast::BinaryOp::kAdd, arg_lhs, arg_rhs, Source{{12, 34}},
+                                /* is_compound */ false);
+
+    bool matched = result.result != nullptr;
+    bool expected_match = GetParam().expected_match;
+    EXPECT_EQ(matched, expected_match) << Diagnostics().str();
+
+    auto* expected_result = GetParam().expected_result(*this);
+    EXPECT_TYPE(result.result, expected_result);
+
+    auto* expected_param_lhs = GetParam().expected_param_lhs(*this);
+    EXPECT_TYPE(result.lhs, expected_param_lhs);
+
+    auto* expected_param_rhs = GetParam().expected_param_rhs(*this);
+    EXPECT_TYPE(result.rhs, expected_param_rhs);
+}
+
+INSTANTIATE_TEST_SUITE_P(AFloat_AInt,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<f32,        f32,        f32,        AFloat,     AFloat>(),
+Case::Create<f32,        f32,        f32,        AFloat,     AInt>(),
+Case::Create<f32,        f32,        f32,        AInt,       AFloat>(),
+Case::Create<i32,        i32,        i32,        AInt,       AInt>()
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAFloat_VecAInt,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<f32V,       f32V,       f32V,       AFloatV,    AFloatV>(),
+Case::Create<f32V,       f32V,       f32V,       AFloatV,    AIntV>(),
+Case::Create<f32V,       f32V,       f32V,       AIntV,      AFloatV>(),
+Case::Create<i32V,       i32V,       i32V,       AIntV,      AIntV>()
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(AFloat_f32,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<f32,        f32,        f32,        AFloat,     f32>(),
+Case::Create<f32,        f32,        f32,        f32,        AFloat>()
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAFloat_Vecf32,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<f32V,       f32V,       f32V,       AFloatV,    f32V>(),
+Case::Create<f32V,       f32V,       f32V,       f32V,       AFloatV>()
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(
+    AFloat_i32,
+    IntrinsicTableAbstractBinaryTest,
+    testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<void,        void,        void,        AFloat,     i32>(false),
+Case::Create<void,        void,        void,        i32,        AFloat>(false)
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(
+    VecAFloat_Veci32,
+    IntrinsicTableAbstractBinaryTest,
+    testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<void,        void,        void,        AFloatV,    i32V>(false),
+Case::Create<void,        void,        void,        i32V,       AFloatV>(false)
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(
+    AFloat_u32,
+    IntrinsicTableAbstractBinaryTest,
+    testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<void,        void,        void,        AFloat,     u32>(false),
+Case::Create<void,        void,        void,        u32,        AFloat>(false)
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(
+    VecAFloat_Vecu32,
+    IntrinsicTableAbstractBinaryTest,
+    testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<void,        void,        void,        AFloatV,    u32V>(false),
+Case::Create<void,        void,        void,        u32V,       AFloatV>(false)
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(AInt_f32,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<f32,        f32,        f32,        AInt,       f32>(),
+Case::Create<f32,        f32,        f32,        f32,        AInt>()
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAInt_Vecf32,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<f32V,       f32V,       f32V,       AIntV,      f32V>(),
+Case::Create<f32V,       f32V,       f32V,       f32V,       AIntV>()
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(AInt_i32,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<i32,        i32,        i32,        AInt,       i32>(),
+Case::Create<i32,        i32,        i32,        i32,        AInt>()
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAInt_Veci32,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<i32V,       i32V,       i32V,       AIntV,      i32V>(),
+Case::Create<i32V,       i32V,       i32V,       i32V,       AIntV>()
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(AInt_u32,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<u32,        u32,        u32,        AInt,       u32>(),
+Case::Create<u32,        u32,        u32,        u32,        AInt>()
+                             ));  // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAInt_Vecu32,
+                         IntrinsicTableAbstractBinaryTest,
+                         testing::Values(  // clang-format off
+//            result   | param lhs | param rhs |  arg lhs  |  arg rhs
+Case::Create<u32V,       u32V,       u32V,       AIntV,      u32V>(),
+Case::Create<u32V,       u32V,       u32V,       u32V,       AIntV>()
+                             ));  // clang-format on
+
+}  // namespace AbstractBinaryTests
+
+////////////////////////////////////////////////////////////////////////////////
+// AbstractTernaryTests
+////////////////////////////////////////////////////////////////////////////////
+namespace AbstractTernaryTests {
+
+struct Case {
+    template <typename RESULT,
+              typename PARAM_A,
+              typename PARAM_B,
+              typename PARAM_C,
+              typename ARG_A,
+              typename ARG_B,
+              typename ARG_C>
+    static Case Create(bool match = true) {
+        return {
+            match,
+            builder::DataType<RESULT>::Sem,   //
+            builder::DataType<PARAM_A>::Sem,  //
+            builder::DataType<PARAM_B>::Sem,  //
+            builder::DataType<PARAM_C>::Sem,  //
+            builder::DataType<ARG_A>::Sem,    //
+            builder::DataType<ARG_B>::Sem,    //
+            builder::DataType<ARG_C>::Sem,    //
+        };
+    }
+    bool expected_match;
+    builder::sem_type_func_ptr expected_result;
+    builder::sem_type_func_ptr expected_param_a;
+    builder::sem_type_func_ptr expected_param_b;
+    builder::sem_type_func_ptr expected_param_c;
+    builder::sem_type_func_ptr arg_a;
+    builder::sem_type_func_ptr arg_b;
+    builder::sem_type_func_ptr arg_c;
+};
+
+struct IntrinsicTableAbstractTernaryTest : public ResolverTestWithParam<Case> {
+    std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
+};
+
+TEST_P(IntrinsicTableAbstractTernaryTest, MatchClamp) {
+    auto* arg_a = GetParam().arg_a(*this);
+    auto* arg_b = GetParam().arg_b(*this);
+    auto* arg_c = GetParam().arg_c(*this);
+    auto* builtin =
+        table->Lookup(sem::BuiltinType::kClamp, {arg_a, arg_b, arg_c}, Source{{12, 34}});
+
+    bool matched = builtin != nullptr;
+    bool expected_match = GetParam().expected_match;
+    EXPECT_EQ(matched, expected_match) << Diagnostics().str();
+
+    auto* result = builtin ? builtin->ReturnType() : nullptr;
+    auto* expected_result = GetParam().expected_result(*this);
+    EXPECT_TYPE(result, expected_result);
+
+    auto* param_a = builtin ? builtin->Parameters()[0]->Type() : nullptr;
+    auto* expected_param_a = GetParam().expected_param_a(*this);
+    EXPECT_TYPE(param_a, expected_param_a);
+
+    auto* param_b = builtin ? builtin->Parameters()[1]->Type() : nullptr;
+    auto* expected_param_b = GetParam().expected_param_b(*this);
+    EXPECT_TYPE(param_b, expected_param_b);
+
+    auto* param_c = builtin ? builtin->Parameters()[2]->Type() : nullptr;
+    auto* expected_param_c = GetParam().expected_param_c(*this);
+    EXPECT_TYPE(param_c, expected_param_c);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AFloat_AInt,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<f32,      f32,      f32,      f32,      AFloat,   AFloat,   AFloat>(),
+Case::Create<f32,      f32,      f32,      f32,      AFloat,   AFloat,   AInt>(),
+Case::Create<f32,      f32,      f32,      f32,      AFloat,   AInt,     AFloat>(),
+Case::Create<f32,      f32,      f32,      f32,      AFloat,   AInt,     AInt>(),
+Case::Create<f32,      f32,      f32,      f32,      AInt,     AFloat,   AFloat>(),
+Case::Create<f32,      f32,      f32,      f32,      AInt,     AFloat,   AInt>(),
+Case::Create<f32,      f32,      f32,      f32,      AInt,     AInt,     AFloat>(),
+Case::Create<i32,      i32,      i32,      i32,      AInt,     AInt,     AInt>()
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    VecAFloat_VecAInt,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<f32V,     f32V,     f32V,     f32V,     AFloatV,  AFloatV,  AFloatV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AFloatV,  AFloatV,  AIntV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AFloatV,  AIntV,    AFloatV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AFloatV,  AIntV,    AIntV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AIntV,    AFloatV,  AFloatV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AIntV,    AFloatV,  AIntV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AIntV,    AIntV,    AFloatV>(),
+Case::Create<i32V,     i32V,     i32V,     i32V,     AIntV,    AIntV,    AIntV>()
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    AFloat_f32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<f32,      f32,      f32,      f32,      AFloat,   AFloat,   f32>(),
+Case::Create<f32,      f32,      f32,      f32,      AFloat,   f32,      AFloat>(),
+Case::Create<f32,      f32,      f32,      f32,      AFloat,   f32,      f32>(),
+Case::Create<f32,      f32,      f32,      f32,      f32,      AFloat,   AFloat>(),
+Case::Create<f32,      f32,      f32,      f32,      f32,      AFloat,   f32>(),
+Case::Create<f32,      f32,      f32,      f32,      f32,      f32,      AFloat>()
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    VecAFloat_Vecf32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<f32V,     f32V,     f32V,     f32V,     AFloatV,  AFloatV,  f32V>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AFloatV,  f32V,     AFloatV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AFloatV,  f32V,     f32V>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     f32V,     AFloatV,  AFloatV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     f32V,     AFloatV,  f32V>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     f32V,     f32V,     AFloatV> ()
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    AFloat_i32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<void,     void,     void,     void,     AFloat,   AFloat,   i32>(false),
+Case::Create<void,     void,     void,     void,     AFloat,   i32,      AFloat>(false),
+Case::Create<void,     void,     void,     void,     AFloat,   i32,      i32>(false),
+Case::Create<void,     void,     void,     void,     i32,      AFloat,   AFloat>(false),
+Case::Create<void,     void,     void,     void,     i32,      AFloat,   i32>(false),
+Case::Create<void,     void,     void,     void,     i32,      i32,      AFloat>(false)
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    VecAFloat_Veci32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<void,     void,     void,     void,     AFloatV,  AFloatV,  i32V>(false),
+Case::Create<void,     void,     void,     void,     AFloatV,  i32V,     AFloatV>(false),
+Case::Create<void,     void,     void,     void,     AFloatV,  i32V,     i32V>(false),
+Case::Create<void,     void,     void,     void,     i32V,     AFloatV,  AFloatV>(false),
+Case::Create<void,     void,     void,     void,     i32V,     AFloatV,  i32V>(false),
+Case::Create<void,     void,     void,     void,     i32V,     i32V,     AFloatV>(false)
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    AFloat_u32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<void,     void,     void,     void,     AFloat,   AFloat,   u32>(false),
+Case::Create<void,     void,     void,     void,     AFloat,   u32,      AFloat>(false),
+Case::Create<void,     void,     void,     void,     AFloat,   u32,      u32>(false),
+Case::Create<void,     void,     void,     void,     u32,      AFloat,   AFloat>(false),
+Case::Create<void,     void,     void,     void,     u32,      AFloat,   u32>(false),
+Case::Create<void,     void,     void,     void,     u32,      u32,      AFloat>(false)
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    VecAFloat_Vecu32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<void,     void,     void,     void,     AFloatV,  AFloatV,  u32V>(false),
+Case::Create<void,     void,     void,     void,     AFloatV,  u32V,     AFloatV>(false),
+Case::Create<void,     void,     void,     void,     AFloatV,  u32V,     u32V>(false),
+Case::Create<void,     void,     void,     void,     u32V,     AFloatV,  AFloatV>(false),
+Case::Create<void,     void,     void,     void,     u32V,     AFloatV,  u32V>(false),
+Case::Create<void,     void,     void,     void,     u32V,     u32V,     AFloatV>(false)
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    AInt_f32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<f32,      f32,      f32,      f32,      AInt,     AInt,     f32>(),
+Case::Create<f32,      f32,      f32,      f32,      AInt,     f32,      AInt>(),
+Case::Create<f32,      f32,      f32,      f32,      AInt,     f32,      f32>(),
+Case::Create<f32,      f32,      f32,      f32,      f32,      AInt,     AInt>(),
+Case::Create<f32,      f32,      f32,      f32,      f32,      AInt,     f32>(),
+Case::Create<f32,      f32,      f32,      f32,      f32,      f32,      AInt>()
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    VecAInt_Vecf32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<f32V,     f32V,     f32V,     f32V,     AIntV,    AIntV,    f32V>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AIntV,    f32V,     AIntV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     AIntV,    f32V,     f32V>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     f32V,     AIntV,    AIntV>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     f32V,     AIntV,    f32V>(),
+Case::Create<f32V,     f32V,     f32V,     f32V,     f32V,     f32V,     AIntV>()
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    AInt_i32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<i32,      i32,      i32,      i32,      AInt,     AInt,     i32>(),
+Case::Create<i32,      i32,      i32,      i32,      AInt,     i32,      AInt>(),
+Case::Create<i32,      i32,      i32,      i32,      AInt,     i32,      i32>(),
+Case::Create<i32,      i32,      i32,      i32,      i32,      AInt,     AInt>(),
+Case::Create<i32,      i32,      i32,      i32,      i32,      AInt,     i32>(),
+Case::Create<i32,      i32,      i32,      i32,      i32,      i32,      AInt>()
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    VecAInt_Veci32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<i32V,     i32V,     i32V,     i32V,     AIntV,    AIntV,     i32V>(),
+Case::Create<i32V,     i32V,     i32V,     i32V,     AIntV,    i32V,      AIntV>(),
+Case::Create<i32V,     i32V,     i32V,     i32V,     AIntV,    i32V,      i32V>(),
+Case::Create<i32V,     i32V,     i32V,     i32V,     i32V,     AIntV,     AIntV>(),
+Case::Create<i32V,     i32V,     i32V,     i32V,     i32V,     AIntV,     i32V>(),
+Case::Create<i32V,     i32V,     i32V,     i32V,     i32V,     i32V,      AIntV>()
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    AInt_u32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<u32,      u32,      u32,      u32,      AInt,     AInt,     u32>(),
+Case::Create<u32,      u32,      u32,      u32,      AInt,     u32,      AInt>(),
+Case::Create<u32,      u32,      u32,      u32,      AInt,     u32,      u32>(),
+Case::Create<u32,      u32,      u32,      u32,      u32,      AInt,     AInt>(),
+Case::Create<u32,      u32,      u32,      u32,      u32,      AInt,     u32>(),
+Case::Create<u32,      u32,      u32,      u32,      u32,      u32,      AInt>()
+        // clang-format on
+        ));
+
+INSTANTIATE_TEST_SUITE_P(
+    VecAInt_Vecu32,
+    IntrinsicTableAbstractTernaryTest,
+    testing::Values(  // clang-format off
+//           result  | param a | param b | param c |  arg a  |  arg b  |  arg c
+Case::Create<u32V,     u32V,     u32V,     u32V,     AIntV,    AIntV,    u32V>(),
+Case::Create<u32V,     u32V,     u32V,     u32V,     AIntV,    u32V,     AIntV>(),
+Case::Create<u32V,     u32V,     u32V,     u32V,     AIntV,    u32V,     u32V>(),
+Case::Create<u32V,     u32V,     u32V,     u32V,     u32V,     AIntV,    AIntV>(),
+Case::Create<u32V,     u32V,     u32V,     u32V,     u32V,     AIntV,    u32V>(),
+Case::Create<u32V,     u32V,     u32V,     u32V,     u32V,     u32V,     AIntV>()
+        // clang-format on
+        ));
+
+}  // namespace AbstractTernaryTests
+
 }  // namespace
-}  // namespace tint
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 4183b44..270acf1 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -50,6 +50,9 @@
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/ast/vector.h"
 #include "src/tint/ast/workgroup_attribute.h"
+#include "src/tint/resolver/uniformity.h"
+#include "src/tint/sem/abstract_float.h"
+#include "src/tint/sem/abstract_int.h"
 #include "src/tint/sem/array.h"
 #include "src/tint/sem/atomic.h"
 #include "src/tint/sem/call.h"
@@ -81,12 +84,13 @@
 
 namespace tint::resolver {
 
-Resolver::Resolver(ProgramBuilder* builder)
+Resolver::Resolver(ProgramBuilder* builder, bool enable_abstract_numerics)
     : builder_(builder),
       diagnostics_(builder->Diagnostics()),
       intrinsic_table_(IntrinsicTable::Create(*builder)),
       sem_(builder, dependencies_),
-      validator_(builder, sem_) {}
+      validator_(builder, sem_),
+      enable_abstract_numerics_(enable_abstract_numerics) {}
 
 Resolver::~Resolver() = default;
 
@@ -100,9 +104,6 @@
         return false;
     }
 
-    // Create the semantic module
-    builder_->Sem().SetModule(builder_->create<sem::Module>(dependencies_.ordered_globals));
-
     bool result = ResolveInternal();
 
     if (!result && !diagnostics_.contains_errors()) {
@@ -110,6 +111,10 @@
         return false;
     }
 
+    // Create the semantic module
+    builder_->Sem().SetModule(builder_->create<sem::Module>(
+        std::move(dependencies_.ordered_globals), std::move(enabled_extensions_)));
+
     return result;
 }
 
@@ -119,19 +124,16 @@
     // Process all module-scope declarations in dependency order.
     for (auto* decl : dependencies_.ordered_globals) {
         Mark(decl);
-        // Enable directives don't have sem node.
-        if (decl->Is<ast::Enable>()) {
-            continue;
-        }
-        if (!Switch(
+        if (!Switch<bool>(
                 decl,  //
+                [&](const ast::Enable* e) { return Enable(e); },
                 [&](const ast::TypeDecl* td) { return TypeDecl(td); },
                 [&](const ast::Function* func) { return Function(func); },
                 [&](const ast::Variable* var) { return GlobalVariable(var); },
                 [&](Default) {
                     TINT_UNREACHABLE(Resolver, diagnostics_)
                         << "unhandled global declaration: " << decl->TypeInfo().name;
-                    return nullptr;
+                    return false;
                 })) {
             return false;
         }
@@ -145,6 +147,12 @@
         return false;
     }
 
+    if (!enabled_extensions_.contains(ast::Extension::kChromiumDisableUniformityAnalysis)) {
+        if (!AnalyzeUniformity(builder_, dependencies_)) {
+            // TODO(jrprice): Reject programs that fail uniformity analysis.
+        }
+    }
+
     bool result = true;
     for (auto* node : builder_->ASTNodes().Objects()) {
         if (marked_.count(node) == 0) {
@@ -167,6 +175,14 @@
         [&](const ast::Bool*) { return builder_->create<sem::Bool>(); },
         [&](const ast::I32*) { return builder_->create<sem::I32>(); },
         [&](const ast::U32*) { return builder_->create<sem::U32>(); },
+        [&](const ast::F16* t) -> sem::F16* {
+            // Validate if f16 type is allowed.
+            if (!enabled_extensions_.contains(ast::Extension::kF16)) {
+                AddError("f16 used without 'f16' extension enabled", t->source);
+                return nullptr;
+            }
+            return builder_->create<sem::F16>();
+        },
         [&](const ast::F32*) { return builder_->create<sem::F32>(); },
         [&](const ast::Vector* t) -> sem::Vector* {
             if (!t->type) {
@@ -780,13 +796,12 @@
             continue;
         }
         // validator_.Validate and set the default value for this dimension.
-        if (is_i32 ? value.Elements()[0].i32 < 1 : value.Elements()[0].u32 < 1) {
+        if (value.Element<AInt>(0).value < 1) {
             AddError("workgroup_size argument must be at least 1", values[i]->source);
             return false;
         }
 
-        ws[i].value =
-            is_i32 ? static_cast<uint32_t>(value.Elements()[0].i32) : value.Elements()[0].u32;
+        ws[i].value = static_cast<uint32_t>(value.Element<AInt>(0).value);
     }
 
     current_function_->SetWorkgroupSize(std::move(ws));
@@ -858,10 +873,13 @@
     auto* sem =
         builder_->create<sem::CaseStatement>(stmt, current_compound_statement_, current_function_);
     return StatementScope(stmt, sem, [&] {
+        sem->Selectors().reserve(stmt->selectors.size());
         for (auto* sel : stmt->selectors) {
-            if (!Expression(sel)) {
+            auto* expr = Expression(sel);
+            if (!expr) {
                 return false;
             }
+            sem->Selectors().emplace_back(expr);
         }
         Mark(stmt->body);
         auto* body = BlockStatement(stmt->body);
@@ -935,17 +953,15 @@
 
             if (stmt->continuing) {
                 Mark(stmt->continuing);
-                if (!stmt->continuing->Empty()) {
-                    auto* continuing = StatementScope(
-                        stmt->continuing,
-                        builder_->create<sem::LoopContinuingBlockStatement>(
-                            stmt->continuing, current_compound_statement_, current_function_),
-                        [&] { return Statements(stmt->continuing->statements); });
-                    if (!continuing) {
-                        return false;
-                    }
-                    behaviors.Add(continuing->Behaviors());
+                auto* continuing = StatementScope(
+                    stmt->continuing,
+                    builder_->create<sem::LoopContinuingBlockStatement>(
+                        stmt->continuing, current_compound_statement_, current_function_),
+                    [&] { return Statements(stmt->continuing->statements); });
+                if (!continuing) {
+                    return false;
                 }
+                behaviors.Add(continuing->Behaviors());
             }
 
             if (behaviors.Contains(sem::Behavior::kBreak)) {  // Does the loop exit?
@@ -1014,11 +1030,19 @@
 
 sem::Expression* Resolver::Expression(const ast::Expression* root) {
     std::vector<const ast::Expression*> sorted;
-    bool mark_failed = false;
+    constexpr size_t kMaxExpressionDepth = 512U;
+    bool failed = false;
     if (!ast::TraverseExpressions<ast::TraverseOrder::RightToLeft>(
-            root, diagnostics_, [&](const ast::Expression* expr) {
+            root, diagnostics_, [&](const ast::Expression* expr, size_t depth) {
+                if (depth > kMaxExpressionDepth) {
+                    AddError(
+                        "reached max expression depth of " + std::to_string(kMaxExpressionDepth),
+                        expr->source);
+                    failed = true;
+                    return ast::TraverseAction::Stop;
+                }
                 if (!Mark(expr)) {
-                    mark_failed = true;
+                    failed = true;
                     return ast::TraverseAction::Stop;
                 }
                 sorted.emplace_back(expr);
@@ -1027,7 +1051,7 @@
         return nullptr;
     }
 
-    if (mark_failed) {
+    if (failed) {
         return nullptr;
     }
 
@@ -1137,164 +1161,211 @@
 }
 
 sem::Call* Resolver::Call(const ast::CallExpression* expr) {
+    // A CallExpression can resolve to one of:
+    // * A function call.
+    // * A builtin call.
+    // * A type constructor.
+    // * A type conversion.
+
+    // Resolve all of the arguments, their types and the set of behaviors.
     std::vector<const sem::Expression*> args(expr->args.size());
-    std::vector<const sem::Type*> arg_tys(args.size());
     sem::Behaviors arg_behaviors;
-
-    // The element type of all the arguments. Nullptr if argument types are
-    // different.
-    const sem::Type* arg_el_ty = nullptr;
-
     for (size_t i = 0; i < expr->args.size(); i++) {
         auto* arg = sem_.Get(expr->args[i]);
         if (!arg) {
             return nullptr;
         }
         args[i] = arg;
-        arg_tys[i] = args[i]->Type();
         arg_behaviors.Add(arg->Behaviors());
-
-        // Determine the common argument element type
-        auto* el_ty = arg_tys[i]->UnwrapRef();
-        if (auto* vec = el_ty->As<sem::Vector>()) {
-            el_ty = vec->type();
-        } else if (auto* mat = el_ty->As<sem::Matrix>()) {
-            el_ty = mat->type();
-        }
-        if (i == 0) {
-            arg_el_ty = el_ty;
-        } else if (arg_el_ty != el_ty) {
-            arg_el_ty = nullptr;
-        }
     }
-
     arg_behaviors.Remove(sem::Behavior::kNext);
 
-    auto type_ctor_or_conv = [&](const sem::Type* ty) -> sem::Call* {
-        // The call has resolved to a type constructor or cast.
-        if (args.size() == 1) {
-            auto* target = ty;
-            auto* source = args[0]->Type()->UnwrapRef();
-            if ((source != target) &&  //
-                ((source->is_scalar() && target->is_scalar()) ||
-                 (source->Is<sem::Vector>() && target->Is<sem::Vector>()) ||
-                 (source->Is<sem::Matrix>() && target->Is<sem::Matrix>()))) {
-                // Note: Matrix types currently cannot be converted (the element type
-                // must only be f32). We implement this for the day we support other
-                // matrix element types.
-                return TypeConversion(expr, ty, args[0], arg_tys[0]);
-            }
+    // Did any arguments have side effects?
+    bool has_side_effects =
+        std::any_of(args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); });
+
+    // ct_ctor_or_conv is a helper for building either a sem::TypeConstructor or sem::TypeConversion
+    // call for a CtorConvIntrinsic with an optional template argument type.
+    auto ct_ctor_or_conv = [&](CtorConvIntrinsic ty, const sem::Type* template_arg) -> sem::Call* {
+        auto arg_tys = utils::Transform(args, [](auto* arg) { return arg->Type(); });
+        auto* call_target = intrinsic_table_->Lookup(ty, template_arg, arg_tys, expr->source);
+        if (!call_target) {
+            return nullptr;
         }
-        return TypeConstructor(expr, ty, std::move(args), std::move(arg_tys));
+        auto value = EvaluateConstantValue(expr, call_target->ReturnType());
+        return builder_->create<sem::Call>(expr, call_target, std::move(args), current_statement_,
+                                           value, has_side_effects);
     };
 
-    // Resolve the target of the CallExpression to determine whether this is a
-    // function call, cast or type constructor expression.
-    if (expr->target.type) {
-        const sem::Type* ty = nullptr;
-
-        auto err_cannot_infer_el_ty = [&](std::string name) {
-            AddError("cannot infer " + name +
-                         " element type, as constructor arguments have different types",
-                     expr->source);
-            for (size_t i = 0; i < args.size(); i++) {
-                auto* arg = args[i];
-                AddNote("argument " + std::to_string(i) + " has type " +
-                            arg->Type()->FriendlyName(builder_->Symbols()),
-                        arg->Declaration()->source);
-            }
-        };
-
-        if (!expr->args.empty()) {
-            // vecN() without explicit element type?
-            // Try to infer element type from args
-            if (auto* vec = expr->target.type->As<ast::Vector>()) {
-                if (!vec->type) {
-                    if (!arg_el_ty) {
-                        err_cannot_infer_el_ty("vector");
-                        return nullptr;
-                    }
-
-                    Mark(vec);
-                    auto* v =
-                        builder_->create<sem::Vector>(arg_el_ty, static_cast<uint32_t>(vec->width));
-                    if (!validator_.Vector(v, vec->source)) {
-                        return nullptr;
-                    }
-                    builder_->Sem().Add(vec, v);
-                    ty = v;
-                }
-            }
-
-            // matNxM() without explicit element type?
-            // Try to infer element type from args
-            if (auto* mat = expr->target.type->As<ast::Matrix>()) {
-                if (!mat->type) {
-                    if (!arg_el_ty) {
-                        err_cannot_infer_el_ty("matrix");
-                        return nullptr;
-                    }
-
-                    Mark(mat);
-                    auto* column_type = builder_->create<sem::Vector>(arg_el_ty, mat->rows);
-                    auto* m = builder_->create<sem::Matrix>(column_type, mat->columns);
-                    if (!validator_.Matrix(m, mat->source)) {
-                        return nullptr;
-                    }
-                    builder_->Sem().Add(mat, m);
-                    ty = m;
-                }
-            }
-        }
-
-        if (ty == nullptr) {
-            ty = Type(expr->target.type);
-            if (!ty) {
+    // ct_ctor_or_conv is a helper for building either a sem::TypeConstructor or sem::TypeConversion
+    // call for the given semantic type.
+    auto ty_ctor_or_conv = [&](const sem::Type* ty) {
+        return Switch(
+            ty,  //
+            [&](const sem::Vector* v) {
+                return ct_ctor_or_conv(VectorCtorConvIntrinsic(v->Width()), v->type());
+            },
+            [&](const sem::Matrix* m) {
+                return ct_ctor_or_conv(MatrixCtorConvIntrinsic(m->columns(), m->rows()), m->type());
+            },
+            [&](const sem::I32*) { return ct_ctor_or_conv(CtorConvIntrinsic::kI32, nullptr); },
+            [&](const sem::U32*) { return ct_ctor_or_conv(CtorConvIntrinsic::kU32, 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* {
+                auto* call_target = utils::GetOrCreate(
+                    array_ctors_, ArrayConstructorSig{{arr, args.size()}},
+                    [&]() -> sem::TypeConstructor* {
+                        sem::ParameterList params(args.size());
+                        for (size_t i = 0; i < args.size(); i++) {
+                            params[i] = builder_->create<sem::Parameter>(
+                                nullptr,                   // declaration
+                                static_cast<uint32_t>(i),  // index
+                                arr->ElemType(),           // type
+                                ast::StorageClass::kNone,  // storage_class
+                                ast::Access::kUndefined);  // access
+                        }
+                        return builder_->create<sem::TypeConstructor>(arr, std::move(params));
+                    });
+                auto value = EvaluateConstantValue(expr, call_target->ReturnType());
+                return builder_->create<sem::Call>(expr, call_target, std::move(args),
+                                                   current_statement_, value, has_side_effects);
+            },
+            [&](const sem::Struct* str) -> sem::Call* {
+                auto* call_target = utils::GetOrCreate(
+                    struct_ctors_, StructConstructorSig{{str, args.size()}},
+                    [&]() -> sem::TypeConstructor* {
+                        sem::ParameterList params(std::min(args.size(), str->Members().size()));
+                        for (size_t i = 0, n = params.size(); i < n; i++) {
+                            params[i] = builder_->create<sem::Parameter>(
+                                nullptr,                    // declaration
+                                static_cast<uint32_t>(i),   // index
+                                str->Members()[i]->Type(),  // type
+                                ast::StorageClass::kNone,   // storage_class
+                                ast::Access::kUndefined);   // access
+                        }
+                        return builder_->create<sem::TypeConstructor>(str, std::move(params));
+                    });
+                auto value = EvaluateConstantValue(expr, call_target->ReturnType());
+                return builder_->create<sem::Call>(expr, call_target, std::move(args),
+                                                   current_statement_, value, has_side_effects);
+            },
+            [&](Default) {
+                AddError("type is not constructible", expr->source);
                 return nullptr;
-            }
-        }
+            });
+    };
 
-        return type_ctor_or_conv(ty);
+    // ast::CallExpression has a target which is either an ast::Type or an ast::IdentifierExpression
+    sem::Call* call = nullptr;
+    if (expr->target.type) {
+        // ast::CallExpression has an ast::Type as the target.
+        // This call is either a type constructor or type conversion.
+        call = Switch(
+            expr->target.type,
+            [&](const ast::Vector* v) -> sem::Call* {
+                Mark(v);
+                // vector element type must be inferred if it was not specified.
+                sem::Type* template_arg = nullptr;
+                if (v->type) {
+                    template_arg = Type(v->type);
+                    if (!template_arg) {
+                        return nullptr;
+                    }
+                }
+                if (auto* c = ct_ctor_or_conv(VectorCtorConvIntrinsic(v->width), template_arg)) {
+                    builder_->Sem().Add(expr->target.type, c->Target()->ReturnType());
+                    return c;
+                }
+                return nullptr;
+            },
+            [&](const ast::Matrix* m) -> sem::Call* {
+                Mark(m);
+                // matrix element type must be inferred if it was not specified.
+                sem::Type* template_arg = nullptr;
+                if (m->type) {
+                    template_arg = Type(m->type);
+                    if (!template_arg) {
+                        return nullptr;
+                    }
+                }
+                if (auto* c = ct_ctor_or_conv(MatrixCtorConvIntrinsic(m->columns, m->rows),
+                                              template_arg)) {
+                    builder_->Sem().Add(expr->target.type, c->Target()->ReturnType());
+                    return c;
+                }
+                return nullptr;
+            },
+            [&](const ast::Type* ast) -> sem::Call* {
+                // Handler for AST types that do not have an optional element type.
+                if (auto* ty = Type(ast)) {
+                    return ty_ctor_or_conv(ty);
+                }
+                return nullptr;
+            },
+            [&](Default) {
+                TINT_ICE(Resolver, diagnostics_)
+                    << expr->source << " unhandled CallExpression target:\n"
+                    << "type: "
+                    << (expr->target.type ? expr->target.type->TypeInfo().name : "<null>");
+                return nullptr;
+            });
+    } else {
+        // ast::CallExpression has an ast::IdentifierExpression as the target.
+        // This call is either a function call, builtin call, type constructor or type conversion.
+        auto* ident = expr->target.name;
+        Mark(ident);
+        auto* resolved = sem_.ResolvedSymbol(ident);
+        call = Switch<sem::Call*>(
+            resolved,  //
+            [&](sem::Type* ty) {
+                // A type constructor or conversions.
+                // Note: Unlike the code path where we're resolving the call target from an
+                // ast::Type, all types must already have the element type explicitly specified, so
+                // there's no need to infer element types.
+                return ty_ctor_or_conv(ty);
+            },
+            [&](sem::Function* func) {
+                return FunctionCall(expr, func, std::move(args), arg_behaviors);
+            },
+            [&](sem::Variable* var) {
+                auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
+                AddError("cannot call variable '" + name + "'", ident->source);
+                AddNote("'" + name + "' declared here", var->Declaration()->source);
+                return nullptr;
+            },
+            [&](Default) -> sem::Call* {
+                auto name = builder_->Symbols().NameFor(ident->symbol);
+                auto builtin_type = sem::ParseBuiltinType(name);
+                if (builtin_type != sem::BuiltinType::kNone) {
+                    return BuiltinCall(expr, builtin_type, std::move(args));
+                }
+
+                TINT_ICE(Resolver, diagnostics_)
+                    << expr->source << " unhandled CallExpression target:\n"
+                    << "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>") << "\n"
+                    << "name: " << builder_->Symbols().NameFor(ident->symbol);
+                return nullptr;
+            });
     }
 
-    auto* ident = expr->target.name;
-    Mark(ident);
+    if (!call) {
+        return nullptr;
+    }
 
-    auto* resolved = sem_.ResolvedSymbol(ident);
-    return Switch(
-        resolved,  //
-        [&](sem::Type* type) { return type_ctor_or_conv(type); },
-        [&](sem::Function* func) {
-            return FunctionCall(expr, func, std::move(args), arg_behaviors);
-        },
-        [&](sem::Variable* var) {
-            auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
-            AddError("cannot call variable '" + name + "'", ident->source);
-            AddNote("'" + name + "' declared here", var->Declaration()->source);
-            return nullptr;
-        },
-        [&](Default) -> sem::Call* {
-            auto name = builder_->Symbols().NameFor(ident->symbol);
-            auto builtin_type = sem::ParseBuiltinType(name);
-            if (builtin_type != sem::BuiltinType::kNone) {
-                return BuiltinCall(expr, builtin_type, std::move(args), std::move(arg_tys));
-            }
-
-            TINT_ICE(Resolver, diagnostics_)
-                << expr->source << " unresolved CallExpression target:\n"
-                << "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>") << "\n"
-                << "name: " << builder_->Symbols().NameFor(ident->symbol);
-            return nullptr;
-        });
+    return validator_.Call(call, current_statement_) ? call : nullptr;
 }
 
 sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
                                  sem::BuiltinType builtin_type,
-                                 const std::vector<const sem::Expression*> args,
-                                 const std::vector<const sem::Type*> arg_tys) {
-    auto* builtin = intrinsic_table_->Lookup(builtin_type, std::move(arg_tys), expr->source);
-    if (!builtin) {
-        return nullptr;
+                                 std::vector<const sem::Expression*> args) {
+    const sem::Builtin* builtin = nullptr;
+    {
+        auto arg_tys = utils::Transform(args, [](auto* arg) { return arg->Type(); });
+        builtin = intrinsic_table_->Lookup(builtin_type, arg_tys, expr->source);
+        if (!builtin) {
+            return nullptr;
+        }
     }
 
     if (builtin->IsDeprecated()) {
@@ -1309,7 +1380,7 @@
 
     current_function_->AddDirectlyCalledBuiltin(builtin);
 
-    if (!validator_.RequiredExtensionForBuiltinFunction(call, builder_->AST().Extensions())) {
+    if (!validator_.RequiredExtensionForBuiltinFunction(call, enabled_extensions_)) {
         return nullptr;
     }
 
@@ -1317,21 +1388,7 @@
         if (!validator_.TextureBuiltinFunction(call)) {
             return nullptr;
         }
-        // Collect a texture/sampler pair for this builtin.
-        const auto& signature = builtin->Signature();
-        int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
-        if (texture_index == -1) {
-            TINT_ICE(Resolver, diagnostics_) << "texture builtin without texture parameter";
-        }
-
-        auto* texture = args[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;
-            current_function_->AddTextureSamplerPair(texture, sampler);
-        }
+        CollectTextureSamplerPairs(builtin, call->Arguments());
     }
 
     if (!validator_.BuiltinCall(call)) {
@@ -1343,9 +1400,27 @@
     return call;
 }
 
+void Resolver::CollectTextureSamplerPairs(const sem::Builtin* builtin,
+                                          const std::vector<const sem::Expression*>& args) const {
+    // Collect a texture/sampler pair for this builtin.
+    const auto& signature = builtin->Signature();
+    int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
+    if (texture_index == -1) {
+        TINT_ICE(Resolver, diagnostics_) << "texture builtin without texture parameter";
+    }
+    auto* texture = args[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;
+        current_function_->AddTextureSamplerPair(texture, sampler);
+    }
+}
+
 sem::Call* Resolver::FunctionCall(const ast::CallExpression* expr,
                                   sem::Function* target,
-                                  const std::vector<const sem::Expression*> args,
+                                  std::vector<const sem::Expression*> args,
                                   sem::Behaviors arg_behaviors) {
     auto sym = expr->target.name->symbol;
     auto name = builder_->Symbols().NameFor(sym);
@@ -1371,25 +1446,7 @@
             current_function_->AddTransitivelyReferencedGlobal(var);
         }
 
-        // Map all texture/sampler pairs from the target function to the
-        // current function. These can only be global or parameter
-        // variables. Resolve any parameter variables to the corresponding
-        // argument passed to the current function. Leave global variables
-        // as-is. Then add the mapped pair to the current function's list of
-        // texture/sampler pairs.
-        for (sem::VariablePair pair : target->TextureSamplerPairs()) {
-            const sem::Variable* texture = pair.first;
-            const sem::Variable* sampler = pair.second;
-            if (auto* param = texture->As<sem::Parameter>()) {
-                texture = args[param->Index()]->As<sem::VariableUser>()->Variable();
-            }
-            if (sampler) {
-                if (auto* param = sampler->As<sem::Parameter>()) {
-                    sampler = args[param->Index()]->As<sem::VariableUser>()->Variable();
-                }
-            }
-            current_function_->AddTextureSamplerPair(texture, sampler);
-        }
+        CollectTextureSamplerPairs(target, call->Arguments());
     }
 
     target->AddCallSite(call);
@@ -1403,131 +1460,27 @@
     return call;
 }
 
-sem::Call* Resolver::TypeConversion(const ast::CallExpression* expr,
-                                    const sem::Type* target,
-                                    const sem::Expression* arg,
-                                    const sem::Type* source) {
-    // It is not valid to have a type-cast call expression inside a call
-    // statement.
-    if (IsCallStatement(expr)) {
-        AddError("type cast evaluated but not used", expr->source);
-        return nullptr;
-    }
-
-    auto* call_target = utils::GetOrCreate(
-        type_conversions_, TypeConversionSig{target, source}, [&]() -> sem::TypeConversion* {
-            // Now that the argument types have been determined, make sure that
-            // they obey the conversion rules laid out in
-            // https://gpuweb.github.io/gpuweb/wgsl/#conversion-expr.
-            bool ok = Switch(
-                target,
-                [&](const sem::Vector* vec_type) {
-                    return validator_.VectorConstructorOrCast(expr, vec_type);
-                },
-                [&](const sem::Matrix* mat_type) {
-                    // Note: Matrix types currently cannot be converted (the element
-                    // type must only be f32). We implement this for the day we
-                    // support other matrix element types.
-                    return validator_.MatrixConstructorOrCast(expr, mat_type);
-                },
-                [&](const sem::Array* arr_type) {
-                    return validator_.ArrayConstructorOrCast(expr, arr_type);
-                },
-                [&](const sem::Struct* struct_type) {
-                    return validator_.StructureConstructorOrCast(expr, struct_type);
-                },
-                [&](Default) {
-                    if (target->is_scalar()) {
-                        return validator_.ScalarConstructorOrCast(expr, target);
-                    }
-                    AddError("type is not constructible", expr->source);
-                    return false;
-                });
-            if (!ok) {
-                return nullptr;
+void Resolver::CollectTextureSamplerPairs(sem::Function* func,
+                                          const std::vector<const sem::Expression*>& args) const {
+    // Map all texture/sampler pairs from the target function to the
+    // current function. These can only be global or parameter
+    // variables. Resolve any parameter variables to the corresponding
+    // argument passed to the current function. Leave global variables
+    // as-is. Then add the mapped pair to the current function's list of
+    // texture/sampler pairs.
+    for (sem::VariablePair pair : func->TextureSamplerPairs()) {
+        const sem::Variable* texture = pair.first;
+        const sem::Variable* sampler = pair.second;
+        if (auto* param = texture->As<sem::Parameter>()) {
+            texture = args[param->Index()]->As<sem::VariableUser>()->Variable();
+        }
+        if (sampler) {
+            if (auto* param = sampler->As<sem::Parameter>()) {
+                sampler = args[param->Index()]->As<sem::VariableUser>()->Variable();
             }
-
-            auto* param =
-                builder_->create<sem::Parameter>(nullptr,                   // declaration
-                                                 0,                         // index
-                                                 source->UnwrapRef(),       // type
-                                                 ast::StorageClass::kNone,  // storage_class
-                                                 ast::Access::kUndefined);  // access
-            return builder_->create<sem::TypeConversion>(target, param);
-        });
-
-    if (!call_target) {
-        return nullptr;
+        }
+        current_function_->AddTextureSamplerPair(texture, sampler);
     }
-
-    auto val = EvaluateConstantValue(expr, target);
-    bool has_side_effects = arg->HasSideEffects();
-    return builder_->create<sem::Call>(expr, call_target, std::vector<const sem::Expression*>{arg},
-                                       current_statement_, val, has_side_effects);
-}
-
-sem::Call* Resolver::TypeConstructor(const ast::CallExpression* expr,
-                                     const sem::Type* ty,
-                                     const std::vector<const sem::Expression*> args,
-                                     const std::vector<const sem::Type*> arg_tys) {
-    // It is not valid to have a type-constructor call expression as a call
-    // statement.
-    if (IsCallStatement(expr)) {
-        AddError("type constructor evaluated but not used", expr->source);
-        return nullptr;
-    }
-
-    auto* call_target = utils::GetOrCreate(
-        type_ctors_, TypeConstructorSig{ty, arg_tys}, [&]() -> sem::TypeConstructor* {
-            // Now that the argument types have been determined, make sure that
-            // they obey the constructor type rules laid out in
-            // https://gpuweb.github.io/gpuweb/wgsl/#type-constructor-expr.
-            bool ok = Switch(
-                ty,
-                [&](const sem::Vector* vec_type) {
-                    return validator_.VectorConstructorOrCast(expr, vec_type);
-                },
-                [&](const sem::Matrix* mat_type) {
-                    return validator_.MatrixConstructorOrCast(expr, mat_type);
-                },
-                [&](const sem::Array* arr_type) {
-                    return validator_.ArrayConstructorOrCast(expr, arr_type);
-                },
-                [&](const sem::Struct* struct_type) {
-                    return validator_.StructureConstructorOrCast(expr, struct_type);
-                },
-                [&](Default) {
-                    if (ty->is_scalar()) {
-                        return validator_.ScalarConstructorOrCast(expr, ty);
-                    }
-                    AddError("type is not constructible", expr->source);
-                    return false;
-                });
-            if (!ok) {
-                return nullptr;
-            }
-
-            return builder_->create<sem::TypeConstructor>(
-                ty, utils::Transform(arg_tys,
-                                     [&](const sem::Type* t, size_t i) -> const sem::Parameter* {
-                                         return builder_->create<sem::Parameter>(
-                                             nullptr,                   // declaration
-                                             static_cast<uint32_t>(i),  // index
-                                             t->UnwrapRef(),            // type
-                                             ast::StorageClass::kNone,  // storage_class
-                                             ast::Access::kUndefined);  // access
-                                     }));
-        });
-
-    if (!call_target) {
-        return nullptr;
-    }
-
-    auto val = EvaluateConstantValue(expr, ty);
-    bool has_side_effects =
-        std::any_of(args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); });
-    return builder_->create<sem::Call>(expr, call_target, std::move(args), current_statement_, val,
-                                       has_side_effects);
 }
 
 sem::Expression* Resolver::Literal(const ast::LiteralExpression* literal) {
@@ -1536,8 +1489,10 @@
         [&](const ast::IntLiteralExpression* i) -> sem::Type* {
             switch (i->suffix) {
                 case ast::IntLiteralExpression::Suffix::kNone:
-                // TODO(crbug.com/tint/1504): This will need to become abstract-int.
-                // For now, treat as 'i32'.
+                    if (enable_abstract_numerics_) {
+                        return builder_->create<sem::AbstractInt>();
+                    }
+                    return builder_->create<sem::I32>();
                 case ast::IntLiteralExpression::Suffix::kI:
                     return builder_->create<sem::I32>();
                 case ast::IntLiteralExpression::Suffix::kU:
@@ -1545,7 +1500,13 @@
             }
             return nullptr;
         },
-        [&](const ast::FloatLiteralExpression*) { return builder_->create<sem::F32>(); },
+        [&](const ast::FloatLiteralExpression* f) -> sem::Type* {
+            if (f->suffix == ast::FloatLiteralExpression::Suffix::kNone &&
+                enable_abstract_numerics_) {
+                return builder_->create<sem::AbstractFloat>();
+            }
+            return builder_->create<sem::F32>();
+        },
         [&](const ast::BoolLiteralExpression*) { return builder_->create<sem::Bool>(); },
         [&](Default) { return nullptr; });
 
@@ -1745,27 +1706,27 @@
 }
 
 sem::Expression* Resolver::Binary(const ast::BinaryExpression* expr) {
-    auto* lhs = sem_.Get(expr->lhs);
-    auto* rhs = sem_.Get(expr->rhs);
+    const auto* lhs = sem_.Get(expr->lhs);
+    const auto* rhs = sem_.Get(expr->rhs);
     auto* lhs_ty = lhs->Type()->UnwrapRef();
     auto* rhs_ty = rhs->Type()->UnwrapRef();
 
-    auto* ty = intrinsic_table_->Lookup(expr->op, lhs_ty, rhs_ty, expr->source, false).result;
-    if (!ty) {
+    auto op = intrinsic_table_->Lookup(expr->op, lhs_ty, rhs_ty, expr->source, false);
+    if (!op.result) {
         return nullptr;
     }
 
-    auto val = EvaluateConstantValue(expr, ty);
+    auto val = EvaluateConstantValue(expr, op.result);
     bool has_side_effects = lhs->HasSideEffects() || rhs->HasSideEffects();
-    auto* sem =
-        builder_->create<sem::Expression>(expr, ty, current_statement_, val, has_side_effects);
+    auto* sem = builder_->create<sem::Expression>(expr, op.result, current_statement_, val,
+                                                  has_side_effects);
     sem->Behaviors() = lhs->Behaviors() + rhs->Behaviors();
 
     return sem;
 }
 
 sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
-    auto* expr = sem_.Get(unary->expr);
+    const auto* expr = sem_.Get(unary->expr);
     auto* expr_ty = expr->Type();
     if (!expr_ty) {
         return nullptr;
@@ -1818,6 +1779,7 @@
             if (!ty) {
                 return nullptr;
             }
+            break;
         }
     }
 
@@ -1828,6 +1790,11 @@
     return sem;
 }
 
+bool Resolver::Enable(const ast::Enable* enable) {
+    enabled_extensions_.add(enable->extension);
+    return true;
+}
+
 sem::Type* Resolver::TypeDecl(const ast::TypeDecl* named_type) {
     sem::Type* result = nullptr;
     if (auto* alias = named_type->As<ast::Alias>()) {
@@ -1932,13 +1899,12 @@
             return nullptr;
         }
 
-        if (ty->is_signed_integer_scalar() ? count_val.Elements()[0].i32 < 1
-                                           : count_val.Elements()[0].u32 < 1u) {
+        if (count_val.Element<AInt>(0).value < 1) {
             AddError("array size must be at least 1", size_source);
             return nullptr;
         }
 
-        count = count_val.Elements()[0].u32;
+        count = static_cast<uint32_t>(count_val.Element<AInt>(0).value);
     }
 
     auto size = std::max<uint64_t>(count, 1) * stride;
@@ -2150,19 +2116,21 @@
         auto& behaviors = current_statement_->Behaviors();
         behaviors = sem::Behavior::kReturn;
 
+        const sem::Type* value_ty = nullptr;
         if (auto* value = stmt->value) {
             auto* expr = Expression(value);
             if (!expr) {
                 return false;
             }
             behaviors.Add(expr->Behaviors() - sem::Behavior::kNext);
+            value_ty = expr->Type()->UnwrapRef();
+        } else {
+            value_ty = builder_->create<sem::Void>();
         }
 
         // Validate after processing the return value expression so that its type
         // is available for validation.
-        auto* ret_type =
-            stmt->value ? sem_.TypeOf(stmt->value)->UnwrapRef() : builder_->create<sem::Void>();
-        return validator_.Return(stmt, current_function_->ReturnType(), ret_type,
+        return validator_.Return(stmt, current_function_->ReturnType(), value_ty,
                                  current_statement_);
     });
 }
@@ -2186,6 +2154,7 @@
                 return false;
             }
             behaviors.Add(c->Behaviors());
+            sem->Cases().emplace_back(c);
         }
 
         if (behaviors.Contains(sem::Behavior::kBreak)) {
@@ -2453,36 +2422,4 @@
     return sem::ParseBuiltinType(name) != sem::BuiltinType::kNone;
 }
 
-bool Resolver::IsCallStatement(const ast::Expression* expr) const {
-    return current_statement_ &&
-           Is<ast::CallStatement>(current_statement_->Declaration(),
-                                  [&](auto* stmt) { return stmt->expr == expr; });
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Resolver::TypeConversionSig
-////////////////////////////////////////////////////////////////////////////////
-bool Resolver::TypeConversionSig::operator==(const TypeConversionSig& rhs) const {
-    return target == rhs.target && source == rhs.source;
-}
-std::size_t Resolver::TypeConversionSig::Hasher::operator()(const TypeConversionSig& sig) const {
-    return utils::Hash(sig.target, sig.source);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Resolver::TypeConstructorSig
-////////////////////////////////////////////////////////////////////////////////
-Resolver::TypeConstructorSig::TypeConstructorSig(const sem::Type* ty,
-                                                 const std::vector<const sem::Type*> params)
-    : type(ty), parameters(params) {}
-Resolver::TypeConstructorSig::TypeConstructorSig(const TypeConstructorSig&) = default;
-Resolver::TypeConstructorSig::~TypeConstructorSig() = default;
-
-bool Resolver::TypeConstructorSig::operator==(const TypeConstructorSig& rhs) const {
-    return type == rhs.type && parameters == rhs.parameters;
-}
-std::size_t Resolver::TypeConstructorSig::Hasher::operator()(const TypeConstructorSig& sig) const {
-    return utils::Hash(sig.type, sig.parameters);
-}
-
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 4b75dc0..1725072 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -17,6 +17,7 @@
 
 #include <memory>
 #include <string>
+#include <tuple>
 #include <unordered_map>
 #include <unordered_set>
 #include <utility>
@@ -74,7 +75,10 @@
   public:
     /// Constructor
     /// @param builder the program builder
-    explicit Resolver(ProgramBuilder* builder);
+    /// @param enable_abstract_numerics if true, then treat unsuffixed integers as abstract integers
+    /// and unsuffixed floats as abstract floats. This is a temporary flag while abstract numerics
+    /// are being developed. Once complete, this will be permanently enabled.
+    explicit Resolver(ProgramBuilder* builder, bool enable_abstract_numerics = false);
 
     /// Destructor
     ~Resolver();
@@ -184,23 +188,14 @@
     sem::Function* Function(const ast::Function*);
     sem::Call* FunctionCall(const ast::CallExpression*,
                             sem::Function* target,
-                            const std::vector<const sem::Expression*> args,
+                            std::vector<const sem::Expression*> args,
                             sem::Behaviors arg_behaviors);
     sem::Expression* Identifier(const ast::IdentifierExpression*);
     sem::Call* BuiltinCall(const ast::CallExpression*,
                            sem::BuiltinType,
-                           const std::vector<const sem::Expression*> args,
-                           const std::vector<const sem::Type*> arg_tys);
+                           std::vector<const sem::Expression*> args);
     sem::Expression* Literal(const ast::LiteralExpression*);
     sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
-    sem::Call* TypeConversion(const ast::CallExpression* expr,
-                              const sem::Type* ty,
-                              const sem::Expression* arg,
-                              const sem::Type* arg_ty);
-    sem::Call* TypeConstructor(const ast::CallExpression* expr,
-                               const sem::Type* ty,
-                               const std::vector<const sem::Expression*> args,
-                               const std::vector<const sem::Type*> arg_tys);
     sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
 
     // Statement resolving methods
@@ -226,6 +221,12 @@
     sem::Statement* VariableDeclStatement(const ast::VariableDeclStatement*);
     bool Statements(const ast::StatementList&);
 
+    // CollectTextureSamplerPairs() collects all the texture/sampler pairs from the target function
+    // / builtin, and records these on the current function by calling AddTextureSamplerPair().
+    void CollectTextureSamplerPairs(sem::Function* func,
+                                    const std::vector<const sem::Expression*>& args) const;
+    void CollectTextureSamplerPairs(const sem::Builtin* builtin,
+                                    const std::vector<const sem::Expression*>& args) const;
 
     /// Resolves the WorkgroupSize for the given function, assigning it to
     /// current_function_
@@ -237,6 +238,10 @@
     /// @param ty the ast::Type
     sem::Type* Type(const ast::Type* ty);
 
+    /// @param enable the enable declaration
+    /// @returns the resolved extension
+    bool Enable(const ast::Enable* enable);
+
     /// @param named_type the named type to resolve
     /// @returns the resolved semantic type
     sem::Type* TypeDecl(const ast::TypeDecl* named_type);
@@ -327,7 +332,9 @@
     //////////////////////////////////////////////////////////////////////////////
     /// Cast `Value` to `target_type`
     /// @return the casted value
-    sem::Constant ConstantCast(const sem::Constant& value, const sem::Type* target_elem_type);
+    sem::Constant ConstantCast(const sem::Constant& value,
+                               const sem::Type* target_type,
+                               const sem::Type* target_element_type = nullptr);
 
     sem::Constant EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type);
     sem::Constant EvaluateConstantValue(const ast::LiteralExpression* literal,
@@ -337,39 +344,13 @@
     /// @returns true if the symbol is the name of a builtin function.
     bool IsBuiltin(Symbol) const;
 
-    /// @returns true if `expr` is the current CallStatement's CallExpression
-    bool IsCallStatement(const ast::Expression* expr) const;
+    // ArrayConstructorSig represents a unique array constructor signature.
+    // It is a tuple of the array type and number of arguments provided.
+    using ArrayConstructorSig = utils::UnorderedKeyWrapper<std::tuple<const sem::Array*, size_t>>;
 
-    struct TypeConversionSig {
-        const sem::Type* target;
-        const sem::Type* source;
-
-        bool operator==(const TypeConversionSig&) const;
-
-        /// Hasher provides a hash function for the TypeConversionSig
-        struct Hasher {
-            /// @param sig the TypeConversionSig to create a hash for
-            /// @return the hash value
-            std::size_t operator()(const TypeConversionSig& sig) const;
-        };
-    };
-
-    struct TypeConstructorSig {
-        const sem::Type* type;
-        const std::vector<const sem::Type*> parameters;
-
-        TypeConstructorSig(const sem::Type* ty, const std::vector<const sem::Type*> params);
-        TypeConstructorSig(const TypeConstructorSig&);
-        ~TypeConstructorSig();
-        bool operator==(const TypeConstructorSig&) const;
-
-        /// Hasher provides a hash function for the TypeConstructorSig
-        struct Hasher {
-            /// @param sig the TypeConstructorSig to create a hash for
-            /// @return the hash value
-            std::size_t operator()(const TypeConstructorSig& sig) const;
-        };
-    };
+    // StructConstructorSig represents a unique structure constructor signature.
+    // It is a tuple of the structure type and number of arguments provided.
+    using StructConstructorSig = utils::UnorderedKeyWrapper<std::tuple<const sem::Struct*, size_t>>;
 
     ProgramBuilder* const builder_;
     diag::List& diagnostics_;
@@ -377,19 +358,20 @@
     DependencyGraph dependencies_;
     SemHelper sem_;
     Validator validator_;
+    ast::Extensions enabled_extensions_;
     std::vector<sem::Function*> entry_points_;
     std::unordered_map<const sem::Type*, const Source&> atomic_composite_info_;
     std::unordered_set<const ast::Node*> marked_;
     std::unordered_map<uint32_t, const sem::Variable*> constant_ids_;
-    std::unordered_map<TypeConversionSig, sem::CallTarget*, TypeConversionSig::Hasher>
-        type_conversions_;
-    std::unordered_map<TypeConstructorSig, sem::CallTarget*, TypeConstructorSig::Hasher>
-        type_ctors_;
+    std::unordered_map<ArrayConstructorSig, sem::CallTarget*> array_ctors_;
+    std::unordered_map<StructConstructorSig, sem::CallTarget*> struct_ctors_;
 
     sem::Function* current_function_ = nullptr;
     sem::Statement* current_statement_ = nullptr;
     sem::CompoundStatement* current_compound_statement_ = nullptr;
     sem::BlockStatement* current_block_ = nullptr;
+
+    bool enable_abstract_numerics_ = false;
 };
 
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver_behavior_test.cc b/src/tint/resolver/resolver_behavior_test.cc
index 9cb26dc..3a3b14f 100644
--- a/src/tint/resolver/resolver_behavior_test.cc
+++ b/src/tint/resolver/resolver_behavior_test.cc
@@ -19,6 +19,7 @@
 #include "src/tint/sem/expression.h"
 #include "src/tint/sem/for_loop_statement.h"
 #include "src/tint/sem/if_statement.h"
+#include "src/tint/sem/switch_statement.h"
 
 using namespace tint::number_suffixes;  // NOLINT
 
diff --git a/src/tint/resolver/resolver_constants.cc b/src/tint/resolver/resolver_constants.cc
index 2a8a1d1..265b06d 100644
--- a/src/tint/resolver/resolver_constants.cc
+++ b/src/tint/resolver/resolver_constants.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/resolver/resolver.h"
 
+#include "src/tint/sem/abstract_float.h"
+#include "src/tint/sem/abstract_int.h"
 #include "src/tint/sem/constant.h"
 #include "src/tint/sem/type_constructor.h"
 #include "src/tint/utils/map.h"
@@ -21,6 +23,33 @@
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::resolver {
+namespace {
+
+sem::Constant::Scalars CastScalars(sem::Constant::Scalars in, const sem::Type* target_type) {
+    sem::Constant::Scalars out;
+    out.reserve(in.size());
+    for (auto v : in) {
+        // TODO(crbug.com/tint/1504): Check that value fits in new type
+        out.emplace_back(Switch<sem::Constant::Scalar>(
+            target_type,  //
+            [&](const sem::AbstractInt*) { return sem::Constant::Cast<AInt>(v); },
+            [&](const sem::AbstractFloat*) { return sem::Constant::Cast<AFloat>(v); },
+            [&](const sem::I32*) { return sem::Constant::Cast<AInt>(v); },
+            [&](const sem::U32*) { return sem::Constant::Cast<AInt>(v); },
+            [&](const sem::F32*) { return sem::Constant::Cast<AFloat>(v); },
+            [&](const sem::F16*) { return sem::Constant::Cast<AFloat>(v); },
+            [&](const sem::Bool*) { return sem::Constant::Cast<bool>(v); },
+            [&](Default) {
+                diag::List diags;
+                TINT_UNREACHABLE(Semantic, diags)
+                    << "invalid element type " << target_type->TypeInfo().name;
+                return sem::Constant::Scalar(false);
+            }));
+    }
+    return out;
+}
+
+}  // namespace
 
 sem::Constant Resolver::EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type) {
     if (auto* e = expr->As<ast::LiteralExpression>()) {
@@ -37,13 +66,10 @@
     return Switch(
         literal,
         [&](const ast::IntLiteralExpression* lit) {
-            if (lit->suffix == ast::IntLiteralExpression::Suffix::kU) {
-                return sem::Constant{type, {u32(lit->value)}};
-            }
-            return sem::Constant{type, {i32(lit->value)}};
+            return sem::Constant{type, {AInt(lit->value)}};
         },
         [&](const ast::FloatLiteralExpression* lit) {
-            return sem::Constant{type, {f32(lit->value)}};
+            return sem::Constant{type, {AFloat(lit->value)}};
         },
         [&](const ast::BoolLiteralExpression* lit) {
             return sem::Constant{type, {lit->value}};
@@ -52,96 +78,80 @@
 
 sem::Constant Resolver::EvaluateConstantValue(const ast::CallExpression* call,
                                               const sem::Type* type) {
-    auto* vec = type->As<sem::Vector>();
-
-    // For now, only fold scalars and vectors
-    if (!type->is_scalar() && !vec) {
+    uint32_t result_size = 0;
+    auto* el_ty = sem::Type::ElementOf(type, &result_size);
+    if (!el_ty) {
         return {};
     }
 
-    auto* elem_type = vec ? vec->type() : type;
-    int result_size = vec ? static_cast<int>(vec->Width()) : 1;
+    // ElementOf() will also return the element type of array, which we do not support.
+    if (type->Is<sem::Array>()) {
+        return {};
+    }
 
     // For zero value init, return 0s
     if (call->args.empty()) {
-        if (elem_type->Is<sem::I32>()) {
-            return sem::Constant(type, sem::Constant::Scalars(result_size, 0_i));
-        }
-        if (elem_type->Is<sem::U32>()) {
-            return sem::Constant(type, sem::Constant::Scalars(result_size, 0_u));
-        }
-        if (elem_type->Is<sem::F32>()) {
-            return sem::Constant(type, sem::Constant::Scalars(result_size, 0_f));
-        }
-        if (elem_type->Is<sem::Bool>()) {
-            return sem::Constant(type, sem::Constant::Scalars(result_size, false));
-        }
+        using Scalars = sem::Constant::Scalars;
+        return Switch(
+            el_ty,
+            [&](const sem::AbstractInt*) {
+                return sem::Constant(type, Scalars(result_size, AInt(0)));
+            },
+            [&](const sem::AbstractFloat*) {
+                return sem::Constant(type, Scalars(result_size, AFloat(0)));
+            },
+            [&](const sem::I32*) { return sem::Constant(type, Scalars(result_size, AInt(0))); },
+            [&](const sem::U32*) { return sem::Constant(type, Scalars(result_size, AInt(0))); },
+            [&](const sem::F32*) { return sem::Constant(type, Scalars(result_size, AFloat(0))); },
+            [&](const sem::F16*) { return sem::Constant(type, Scalars(result_size, AFloat(0))); },
+            [&](const sem::Bool*) { return sem::Constant(type, Scalars(result_size, false)); });
     }
 
-    // Build value for type_ctor from each child value by casting to
-    // type_ctor's type.
+    // Build value for type_ctor from each child value by casting to type_ctor's type.
     sem::Constant::Scalars elems;
     for (auto* expr : call->args) {
         auto* arg = builder_->Sem().Get(expr);
-        if (!arg || !arg->ConstantValue()) {
+        if (!arg) {
             return {};
         }
-        auto cast = ConstantCast(arg->ConstantValue(), elem_type);
-        elems.insert(elems.end(), cast.Elements().begin(), cast.Elements().end());
+        auto value = arg->ConstantValue();
+        if (!value) {
+            return {};
+        }
+        elems.insert(elems.end(), value.Elements().begin(), value.Elements().end());
     }
 
     // Splat single-value initializers
     if (elems.size() == 1) {
-        for (int i = 0; i < result_size - 1; ++i) {
+        for (uint32_t i = 0; i < result_size - 1; ++i) {
             elems.emplace_back(elems[0]);
         }
     }
 
-    return sem::Constant(type, std::move(elems));
+    // Finally cast the elements to the desired type.
+    auto cast = CastScalars(elems, el_ty);
+
+    return sem::Constant(type, std::move(cast));
 }
 
 sem::Constant Resolver::ConstantCast(const sem::Constant& value,
-                                     const sem::Type* target_elem_type) {
-    if (value.ElementType() == target_elem_type) {
+                                     const sem::Type* target_type,
+                                     const sem::Type* target_element_type /* = nullptr */) {
+    if (value.Type() == target_type) {
         return value;
     }
 
-    sem::Constant::Scalars elems;
-    for (size_t i = 0; i < value.Elements().size(); ++i) {
-        elems.emplace_back(Switch<sem::Constant::Scalar>(
-            target_elem_type,
-            [&](const sem::I32*) {
-                return value.WithScalarAt(i, [](auto&& s) {  //
-                    return i32(static_cast<int32_t>(s));
-                });
-            },
-            [&](const sem::U32*) {
-                return value.WithScalarAt(i, [](auto&& s) {  //
-                    return u32(static_cast<uint32_t>(s));
-                });
-            },
-            [&](const sem::F32*) {
-                return value.WithScalarAt(i, [](auto&& s) {  //
-                    return static_cast<f32>(s);
-                });
-            },
-            [&](const sem::Bool*) {
-                return value.WithScalarAt(i, [](auto&& s) {  //
-                    return static_cast<bool>(s);
-                });
-            },
-            [&](Default) {
-                diag::List diags;
-                TINT_UNREACHABLE(Semantic, diags)
-                    << "invalid element type " << target_elem_type->TypeInfo().name;
-                return sem::Constant::Scalar(false);
-            }));
+    if (target_element_type == nullptr) {
+        target_element_type = sem::Type::ElementOf(target_type);
+    }
+    if (target_element_type == nullptr) {
+        return {};
+    }
+    if (value.ElementType() == target_element_type) {
+        return sem::Constant(target_type, value.Elements());
     }
 
-    auto* target_type =
-        value.Type()->Is<sem::Vector>()
-            ? builder_->create<sem::Vector>(target_elem_type, static_cast<uint32_t>(elems.size()))
-            : target_elem_type;
+    auto elems = CastScalars(value.Elements(), target_element_type);
 
     return sem::Constant(target_type, elems);
 }
diff --git a/src/tint/resolver/resolver_constants_test.cc b/src/tint/resolver/resolver_constants_test.cc
index a9624ca..05e6e4c 100644
--- a/src/tint/resolver/resolver_constants_test.cc
+++ b/src/tint/resolver/resolver_constants_test.cc
@@ -39,7 +39,7 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 99);
+    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 99);
 }
 
 TEST_F(ResolverConstantsTest, Scalar_u32) {
@@ -54,7 +54,7 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 99u);
+    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 99u);
 }
 
 TEST_F(ResolverConstantsTest, Scalar_f32) {
@@ -69,7 +69,7 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 9.9f);
+    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, 9.9f);
 }
 
 TEST_F(ResolverConstantsTest, Scalar_bool) {
@@ -84,7 +84,7 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), true);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_ZeroInit_i32) {
@@ -101,9 +101,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 0);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 0);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 0);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_ZeroInit_u32) {
@@ -120,9 +120,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 0u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 0u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 0u);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f32) {
@@ -139,9 +139,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 0.0f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 0.0f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 0.0f);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_ZeroInit_bool) {
@@ -158,9 +158,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, false);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, false);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), false);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(1), false);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(2), false);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Splat_i32) {
@@ -177,9 +177,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 99);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 99);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 99);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Splat_u32) {
@@ -196,9 +196,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 99u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 99u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 99u);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Splat_f32) {
@@ -215,9 +215,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 9.9f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 9.9f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 9.9f);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Splat_bool) {
@@ -234,9 +234,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, true);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), true);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(1), true);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(2), true);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_FullConstruct_i32) {
@@ -253,9 +253,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_FullConstruct_u32) {
@@ -272,9 +272,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 1u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 2u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_FullConstruct_f32) {
@@ -291,9 +291,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 1.f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 2.f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 3.f);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_FullConstruct_bool) {
@@ -310,9 +310,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), true);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(1), false);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(2), true);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_MixConstruct_i32) {
@@ -329,9 +329,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_MixConstruct_u32) {
@@ -348,9 +348,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 1u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 2u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32) {
@@ -367,9 +367,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 1.f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 2.f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 3.f);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_MixConstruct_bool) {
@@ -386,9 +386,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), true);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(1), false);
+    EXPECT_EQ(sem->ConstantValue().Element<bool>(2), true);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Cast_f32_to_32) {
@@ -405,9 +405,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
+    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);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Cast_u32_to_f32) {
@@ -424,9 +424,9 @@
     EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
     EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
     ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 10.f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 20.f);
-    EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 30.f);
+    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);
 }
 
 }  // namespace
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index 3b86a48..07c661c 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -42,6 +42,7 @@
 #include "src/tint/sem/reference.h"
 #include "src/tint/sem/sampled_texture.h"
 #include "src/tint/sem/statement.h"
+#include "src/tint/sem/switch_statement.h"
 #include "src/tint/sem/variable.h"
 
 using ::testing::ElementsAre;
@@ -111,11 +112,11 @@
 
     auto* assign = Assign(lhs, rhs);
     auto* block = Block(assign);
-    ast::CaseSelectorList lit;
-    lit.push_back(Expr(3_i));
-    auto* cse = create<ast::CaseStatement>(lit, block);
+    auto* sel = Expr(3_i);
+    auto* cse = Case(sel, block);
+    auto* def = DefaultCase();
     auto* cond_var = Var("c", ty.i32());
-    auto* sw = Switch(cond_var, cse, DefaultCase());
+    auto* sw = Switch(cond_var, cse, def);
     WrapInFunction(v, cond_var, sw);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -127,6 +128,13 @@
     EXPECT_EQ(StmtOf(lhs), assign);
     EXPECT_EQ(StmtOf(rhs), assign);
     EXPECT_EQ(BlockOf(assign), block);
+    auto* sem = Sem().Get(sw);
+    ASSERT_EQ(sem->Cases().size(), 2u);
+    EXPECT_EQ(sem->Cases()[0]->Declaration(), cse);
+    ASSERT_EQ(sem->Cases()[0]->Selectors().size(), 1u);
+    EXPECT_EQ(sem->Cases()[0]->Selectors()[0]->Declaration(), sel);
+    EXPECT_EQ(sem->Cases()[1]->Declaration(), def);
+    EXPECT_EQ(sem->Cases()[1]->Selectors().size(), 0u);
 }
 
 TEST_F(ResolverTest, Stmt_Block) {
@@ -2098,5 +2106,33 @@
                 ElementsAre(f0, v0, a0, s0, f1, v1, a1, s1, f2, v2, a2, s2));
 }
 
+constexpr size_t kMaxExpressionDepth = 512U;
+
+TEST_F(ResolverTest, MaxExpressionDepth_Pass) {
+    auto* b = Var("b", ty.i32());
+    const ast::Expression* chain = nullptr;
+    for (size_t i = 0; i < kMaxExpressionDepth; ++i) {
+        chain = Add(chain ? chain : Expr("b"), Expr("b"));
+    }
+    auto* a = Let("a", nullptr, chain);
+    WrapInFunction(b, a);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverTest, MaxExpressionDepth_Fail) {
+    auto* b = Var("b", ty.i32());
+    const ast::Expression* chain = nullptr;
+    for (size_t i = 0; i < kMaxExpressionDepth + 1; ++i) {
+        chain = Add(chain ? chain : Expr("b"), Expr("b"));
+    }
+    auto* a = Let("a", nullptr, chain);
+    WrapInFunction(b, a);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_THAT(r()->error(), HasSubstr("error: reached max expression depth of " +
+                                        std::to_string(kMaxExpressionDepth)));
+}
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver_test_helper.cc b/src/tint/resolver/resolver_test_helper.cc
index dd590d9..75a6698 100644
--- a/src/tint/resolver/resolver_test_helper.cc
+++ b/src/tint/resolver/resolver_test_helper.cc
@@ -18,7 +18,8 @@
 
 namespace tint::resolver {
 
-TestHelper::TestHelper() : resolver_(std::make_unique<Resolver>(this)) {}
+TestHelper::TestHelper()
+    : resolver_(std::make_unique<Resolver>(this, /* enable_abstract_numerics */ true)) {}
 
 TestHelper::~TestHelper() = default;
 
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index d54e57c..6c5a74f 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -22,6 +22,8 @@
 #include "gtest/gtest.h"
 #include "src/tint/program_builder.h"
 #include "src/tint/resolver/resolver.h"
+#include "src/tint/sem/abstract_float.h"
+#include "src/tint/sem/abstract_int.h"
 #include "src/tint/sem/expression.h"
 #include "src/tint/sem/statement.h"
 #include "src/tint/sem/variable.h"
@@ -174,6 +176,15 @@
 template <typename T>
 struct DataType {};
 
+/// Helper that represents no-type. Returns nullptr for all static methods.
+template <>
+struct DataType<void> {
+    /// @return nullptr
+    static inline const ast::Type* AST(ProgramBuilder&) { return nullptr; }
+    /// @return nullptr
+    static inline const sem::Type* Sem(ProgramBuilder&) { return nullptr; }
+};
+
 /// Helper for building bool types and expressions
 template <>
 struct DataType<bool> {
@@ -192,6 +203,8 @@
     static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
         return b.Expr(elem_value == 0);
     }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() { return "bool"; }
 };
 
 /// Helper for building i32 types and expressions
@@ -212,6 +225,8 @@
     static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
         return b.Expr(static_cast<i32>(elem_value));
     }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() { return "i32"; }
 };
 
 /// Helper for building u32 types and expressions
@@ -232,6 +247,8 @@
     static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
         return b.Expr(static_cast<u32>(elem_value));
     }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() { return "u32"; }
 };
 
 /// Helper for building f32 types and expressions
@@ -252,6 +269,72 @@
     static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
         return b.Expr(static_cast<f32>(elem_value));
     }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() { return "f32"; }
+};
+
+/// Helper for building f16 types and expressions
+template <>
+struct DataType<f16> {
+    /// false as f16 is not a composite type
+    static constexpr bool is_composite = false;
+
+    /// @param b the ProgramBuilder
+    /// @return a new AST f16 type
+    static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.f16(); }
+    /// @param b the ProgramBuilder
+    /// @return the semantic f16 type
+    static inline const sem::Type* Sem(ProgramBuilder& b) { return b.create<sem::F16>(); }
+    /// @param b the ProgramBuilder
+    /// @param elem_value the value f16 will be initialized with
+    /// @return a new AST f16 literal value expression
+    static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+        return b.Expr(static_cast<f16>(elem_value));
+    }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() { return "f16"; }
+};
+
+/// Helper for building abstract float types and expressions
+template <>
+struct DataType<AFloat> {
+    /// false as AFloat is not a composite type
+    static constexpr bool is_composite = false;
+
+    /// @returns nullptr, as abstract floats are un-typeable
+    static inline const ast::Type* AST(ProgramBuilder&) { return nullptr; }
+    /// @param b the ProgramBuilder
+    /// @return the semantic abstract-float type
+    static inline const sem::Type* Sem(ProgramBuilder& b) { return b.create<sem::AbstractFloat>(); }
+    /// @param b the ProgramBuilder
+    /// @param elem_value the value the abstract-float literal will be constructed with
+    /// @return a new AST abstract-float literal value expression
+    static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+        return b.Expr(AFloat(elem_value));
+    }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() { return "abstract-float"; }
+};
+
+/// Helper for building abstract integer types and expressions
+template <>
+struct DataType<AInt> {
+    /// false as AFloat is not a composite type
+    static constexpr bool is_composite = false;
+
+    /// @returns nullptr, as abstract integers are un-typeable
+    static inline const ast::Type* AST(ProgramBuilder&) { return nullptr; }
+    /// @param b the ProgramBuilder
+    /// @return the semantic abstract-int type
+    static inline const sem::Type* Sem(ProgramBuilder& b) { return b.create<sem::AbstractInt>(); }
+    /// @param b the ProgramBuilder
+    /// @param elem_value the value the abstract-int literal will be constructed with
+    /// @return a new AST abstract-int literal value expression
+    static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+        return b.Expr(AInt(elem_value));
+    }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() { return "abstract-int"; }
 };
 
 /// Helper for building vector types and expressions
@@ -288,6 +371,10 @@
         }
         return args;
     }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() {
+        return "vec" + std::to_string(N) + "<" + DataType<T>::Name() + ">";
+    }
 };
 
 /// Helper for building matrix types and expressions
@@ -325,6 +412,11 @@
         }
         return args;
     }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() {
+        return "mat" + std::to_string(N) + "x" + std::to_string(M) + "<" + DataType<T>::Name() +
+               ">";
+    }
 };
 
 /// Helper for building alias types and expressions
@@ -366,6 +458,8 @@
         // Construct
         return b.Construct(AST(b), DataType<T>::ExprArgs(b, elem_value));
     }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() { return "alias_" + std::to_string(ID); }
 };
 
 /// Helper for building pointer types and expressions
@@ -394,6 +488,8 @@
         b.Global(sym, DataType<T>::AST(b), ast::StorageClass::kPrivate);
         return b.AddressOf(sym);
     }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() { return "ptr<" + DataType<T>::Name() + ">"; }
 };
 
 /// Helper for building array types and expressions
@@ -437,6 +533,10 @@
         }
         return args;
     }
+    /// @returns the WGSL name for the type
+    static inline std::string Name() {
+        return "array<" + DataType<T>::Name() + ", " + std::to_string(N) + ">";
+    }
 };
 
 /// Struct of all creation pointer types
diff --git a/src/tint/resolver/type_constructor_validation_test.cc b/src/tint/resolver/type_constructor_validation_test.cc
index 4c37ee5..3277ee8 100644
--- a/src/tint/resolver/type_constructor_validation_test.cc
+++ b/src/tint/resolver/type_constructor_validation_test.cc
@@ -57,7 +57,7 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferTypeTest_Simple) {
-    // var a = 1;
+    // var a = 1i;
     // var b = a;
     auto* a = Var("a", nullptr, ast::StorageClass::kNone, Expr(1_i));
     auto* b = Var("b", nullptr, ast::StorageClass::kNone, Expr("a"));
@@ -147,15 +147,16 @@
                              << "expected: " << FriendlyName(expected) << "\n";
 }
 static constexpr Params from_arithmetic_expression_cases[] = {
-    ParamsFor<i32>(),       ParamsFor<u32>(),         ParamsFor<f32>(),
-    ParamsFor<vec3<f32>>(), ParamsFor<mat3x3<f32>>(),
-
-    // TODO(amaiorano): Uncomment once https://crbug.com/tint/680 is fixed
-    // ParamsFor<alias<ty_i32>>(),
-    // ParamsFor<alias<ty_u32>>(),
-    // ParamsFor<alias<ty_f32>>(),
-    // ParamsFor<alias<ty_vec3<f32>>>(),
-    // ParamsFor<alias<ty_mat3x3<f32>>>(),
+    ParamsFor<i32>(),
+    ParamsFor<u32>(),
+    ParamsFor<f32>(),
+    ParamsFor<vec3<f32>>(),
+    ParamsFor<mat3x3<f32>>(),
+    ParamsFor<alias<i32>>(),
+    ParamsFor<alias<u32>>(),
+    ParamsFor<alias<f32>>(),
+    ParamsFor<alias<vec3<f32>>>(),
+    ParamsFor<alias<mat3x3<f32>>>(),
 };
 INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
                          InferTypeTest_FromArithmeticExpression,
@@ -234,15 +235,18 @@
 }
 
 static constexpr Params valid_cases[] = {
-    // Direct init (non-conversions)
-    ParamsFor<bool, bool>(Kind::Construct),              //
-    ParamsFor<i32, i32>(Kind::Construct),                //
-    ParamsFor<u32, u32>(Kind::Construct),                //
-    ParamsFor<f32, f32>(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),    //
+    // Identity
+    ParamsFor<bool, bool>(Kind::Construct),                //
+    ParamsFor<i32, i32>(Kind::Construct),                  //
+    ParamsFor<u32, u32>(Kind::Construct),                  //
+    ParamsFor<f32, f32>(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<mat3x3<f32>, mat3x3<f32>>(Kind::Construct),  //
+    ParamsFor<mat2x3<f32>, mat2x3<f32>>(Kind::Construct),  //
+    ParamsFor<mat3x2<f32>, mat3x2<f32>>(Kind::Construct),  //
 
     // Splat
     ParamsFor<vec3<bool>, bool>(Kind::Construct),  //
@@ -250,6 +254,10 @@
     ParamsFor<vec3<u32>, u32>(Kind::Construct),    //
     ParamsFor<vec3<f32>, f32>(Kind::Construct),    //
 
+    ParamsFor<mat3x3<f32>, f32>(Kind::Construct),  //
+    ParamsFor<mat2x3<f32>, f32>(Kind::Construct),  //
+    ParamsFor<mat3x2<f32>, f32>(Kind::Construct),  //
+
     // Conversion
     ParamsFor<bool, u32>(Kind::Conversion),  //
     ParamsFor<bool, i32>(Kind::Conversion),  //
@@ -310,7 +318,7 @@
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     switch (params.kind) {
         case Kind::Construct: {
@@ -408,7 +416,7 @@
     WrapInFunction(a);
 
     ASSERT_FALSE(r()->Resolve());
-    ASSERT_EQ(r()->error(), "12:34 error: expected zero or one value in constructor, got 2");
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for f32(f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, ConversionConstructorInvalid_InvalidInitializer) {
@@ -417,9 +425,8 @@
     WrapInFunction(a);
 
     ASSERT_FALSE(r()->Resolve());
-    ASSERT_EQ(r()->error(),
-              "12:34 error: cannot construct 'f32' with a value of type "
-              "'array<f32, 4>'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for f32(array<f32, 4>)"));
 }
 
 }  // namespace ConversionConstructTest
@@ -433,7 +440,7 @@
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
@@ -449,7 +456,7 @@
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
@@ -622,7 +629,7 @@
     ASSERT_NE(TypeOf(expr), nullptr);
     ASSERT_TRUE(TypeOf(expr)->Is<sem::I32>());
 
-    auto* call = Sem().Get(expr);
+    auto* call = Sem().Get<sem::Call>(expr);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -640,7 +647,7 @@
     ASSERT_NE(TypeOf(expr), nullptr);
     ASSERT_TRUE(TypeOf(expr)->Is<sem::U32>());
 
-    auto* call = Sem().Get(expr);
+    auto* call = Sem().Get<sem::Call>(expr);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -658,7 +665,7 @@
     ASSERT_NE(TypeOf(expr), nullptr);
     ASSERT_TRUE(TypeOf(expr)->Is<sem::F32>());
 
-    auto* call = Sem().Get(expr);
+    auto* call = Sem().Get<sem::Call>(expr);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -676,7 +683,7 @@
     ASSERT_NE(TypeOf(expr), nullptr);
     ASSERT_TRUE(TypeOf(expr)->Is<sem::I32>());
 
-    auto* call = Sem().Get(expr);
+    auto* call = Sem().Get<sem::Call>(expr);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConversion>();
     ASSERT_NE(ctor, nullptr);
@@ -694,7 +701,7 @@
     ASSERT_NE(TypeOf(expr), nullptr);
     ASSERT_TRUE(TypeOf(expr)->Is<sem::U32>());
 
-    auto* call = Sem().Get(expr);
+    auto* call = Sem().Get<sem::Call>(expr);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConversion>();
     ASSERT_NE(ctor, nullptr);
@@ -712,7 +719,7 @@
     ASSERT_NE(TypeOf(expr), nullptr);
     ASSERT_TRUE(TypeOf(expr)->Is<sem::F32>());
 
-    auto* call = Sem().Get(expr);
+    auto* call = Sem().Get<sem::Call>(expr);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConversion>();
     ASSERT_NE(ctor, nullptr);
@@ -727,101 +734,90 @@
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec2F32_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec2<f32>(Expr(Source{{12, 34}}, 1_i), 1_f);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<f32>(), 1_i, 2_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'f32', found 'i32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<f32>(i32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec2U32_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec2<u32>(1_u, Expr(Source{{12, 34}}, 1_i));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<u32>(), 1_u, 2_i));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'u32', found 'i32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<u32>(u32, i32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec2I32_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec2<i32>(Expr(Source{{12, 34}}, 1_u), 1_i);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<i32>(), 1_u, 2_i));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'i32', found 'u32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<i32>(u32, i32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec2Bool_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec2<bool>(true, Expr(Source{{12, 34}}, 1_i));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<bool>(), true, 1_i));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'bool', found 'i32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<bool>(bool, i32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec2_Error_Vec3ArgumentCardinalityTooLarge) {
-    auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<f32>(), vec3<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<f32>(vec3<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec2_Error_Vec4ArgumentCardinalityTooLarge) {
-    auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec4<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<f32>(), vec4<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 4 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<f32>(vec4<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Error_TooManyArgumentsScalar) {
-    auto* tc = vec2<f32>(Expr(Source{{12, 34}}, 1_f), Expr(Source{{12, 40}}, 1_f),
-                         Expr(Source{{12, 46}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<f32>(), 1_f, 2_f, 3_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<f32>(f32, f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Error_TooManyArgumentsVector) {
-    auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
-                         Construct(Source{{12, 40}}, ty.vec2<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<f32>(), vec2<f32>(), vec2<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 4 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec2<f32>(vec2<f32>, vec2<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec2_Error_TooManyArgumentsVectorAndScalar) {
-    auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()), Expr(Source{{12, 40}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<f32>(), vec2<f32>(), 1_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<f32>(vec2<f32>, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Error_InvalidArgumentType) {
-    auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.mat2x2<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<f32>(), mat2x2<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: expected vector or scalar type in vector "
-              "constructor; found: mat2x2<f32>");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<f32>(mat2x2<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Success_ZeroValue) {
@@ -835,7 +831,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -854,7 +850,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -875,7 +871,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -896,7 +892,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -917,7 +913,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -938,7 +934,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -958,7 +954,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConversion>();
     ASSERT_NE(ctor, nullptr);
@@ -969,117 +965,107 @@
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec3F32_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec3<f32>(1_f, 1_f, Expr(Source{{12, 34}}, 1_i));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f32>(), 1_f, 2_f, 3_i));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'f32', found 'i32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<f32>(f32, f32, i32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec3U32_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec3<u32>(1_u, Expr(Source{{12, 34}}, 1_i), 1_u);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<u32>(), 1_u, 2_i, 3_u));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'u32', found 'i32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<u32>(u32, i32, u32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec3I32_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec3<i32>(1_i, Expr(Source{{12, 34}}, 1_u), 1_i);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<i32>(), 1_i, 2_u, 3_i));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'i32', found 'u32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<i32>(i32, u32, i32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec3Bool_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec3<bool>(true, Expr(Source{{12, 34}}, 1_i), false);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<bool>(), false, 1_i, true));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'bool', found 'i32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<bool>(bool, i32, bool)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec3_Error_Vec4ArgumentCardinalityTooLarge) {
-    auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec4<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f32>(), vec4<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<f32>(vec4<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooFewArgumentsScalar) {
-    auto* tc = vec3<f32>(Expr(Source{{12, 34}}, 1_f), Expr(Source{{12, 40}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f32>(), 1_f, 2_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<f32>(f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooManyArgumentsScalar) {
-    auto* tc = vec3<f32>(Expr(Source{{12, 34}}, 1_f), Expr(Source{{12, 40}}, 1_f),
-                         Expr(Source{{12, 46}}, 1_f), Expr(Source{{12, 52}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f32>(), 1_f, 2_f, 3_f, 4_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec3<f32>(f32, f32, f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooFewArgumentsVec2) {
-    auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f32>(), vec2<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<f32>(vec2<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooManyArgumentsVec2) {
-    auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
-                         Construct(Source{{12, 40}}, ty.vec2<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f32>(), vec2<f32>(), vec2<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec3<f32>(vec2<f32>, vec2<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec3_Error_TooManyArgumentsVec2AndScalar) {
-    auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()), Expr(Source{{12, 40}}, 1_f),
-                         Expr(Source{{12, 46}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f32>(), vec2<f32>(), 1_f, 1_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec3<f32>(vec2<f32>, f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooManyArgumentsVec3) {
-    auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()), Expr(Source{{12, 40}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f32>(), vec3<f32>(), 1_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<f32>(vec3<f32>, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_InvalidArgumentType) {
-    auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.mat2x2<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f32>(), mat2x2<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: expected vector or scalar type in vector "
-              "constructor; found: mat2x2<f32>");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<f32>(mat2x2<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Success_ZeroValue) {
@@ -1093,7 +1079,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -1112,7 +1098,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -1134,7 +1120,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -1156,7 +1142,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -1178,7 +1164,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -1200,7 +1186,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -1221,7 +1207,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -1242,7 +1228,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConstructor>();
     ASSERT_NE(ctor, nullptr);
@@ -1262,7 +1248,7 @@
     EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
 
-    auto* call = Sem().Get(tc);
+    auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
     auto* ctor = call->Target()->As<sem::TypeConversion>();
     ASSERT_NE(ctor, nullptr);
@@ -1273,163 +1259,156 @@
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4F32_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec4<f32>(1_f, 1_f, Expr(Source{{12, 34}}, 1_i), 1_f);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), 1_f, 1_f, 1_i, 1_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'f32', found 'i32'");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<f32>(f32, f32, i32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4U32_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec4<u32>(1_u, 1_u, Expr(Source{{12, 34}}, 1_i), 1_u);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<u32>(), 1_u, 1_u, 1_i, 1_u));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'u32', found 'i32'");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<u32>(u32, u32, i32, u32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4I32_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec4<i32>(1_i, 1_i, Expr(Source{{12, 34}}, 1_u), 1_i);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<i32>(), 1_i, 1_i, 1_u, 1_i));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'i32', found 'u32'");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<i32>(i32, i32, u32, i32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4Bool_Error_ScalarArgumentTypeMismatch) {
-    auto* tc = vec4<bool>(true, false, Expr(Source{{12, 34}}, 1_i), true);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<bool>(), true, false, 1_i, true));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'bool', found 'i32'");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<bool>(bool, bool, i32, bool)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Error_TooFewArgumentsScalar) {
-    auto* tc = vec4<f32>(Expr(Source{{12, 34}}, 1_f), Expr(Source{{12, 40}}, 1_f),
-                         Expr(Source{{12, 46}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), 1_f, 2_f, 3_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec4<f32>(f32, f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Error_TooManyArgumentsScalar) {
-    auto* tc = vec4<f32>(Expr(Source{{12, 34}}, 1_f), Expr(Source{{12, 40}}, 1_f),
-                         Expr(Source{{12, 46}}, 1_f), Expr(Source{{12, 52}}, 1_f),
-                         Expr(Source{{12, 58}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), 1_f, 2_f, 3_f, 4_f, 5_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<f32>(f32, f32, f32, f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4_Error_TooFewArgumentsVec2AndScalar) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()), Expr(Source{{12, 40}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), vec2<f32>(), 1_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec4<f32>(vec2<f32>, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4_Error_TooManyArgumentsVec2AndScalars) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()), Expr(Source{{12, 40}}, 1_f),
-                         Expr(Source{{12, 46}}, 1_f), Expr(Source{{12, 52}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), vec2<f32>(), 1_f, 2_f, 3_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<f32>(vec2<f32>, f32, f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4_Error_TooManyArgumentsVec2Vec2Scalar) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
-                         Construct(Source{{12, 40}}, ty.vec2<f32>()), Expr(Source{{12, 46}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), vec2<f32>(), vec2<f32>(), 1_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<f32>(vec2<f32>, vec2<f32>, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4_Error_TooManyArgumentsVec2Vec2Vec2) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
-                         Construct(Source{{12, 40}}, ty.vec2<f32>()),
-                         Construct(Source{{12, 40}}, ty.vec2<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(
+        Construct(Source{{12, 34}}, ty.vec4<f32>(), vec2<f32>(), vec2<f32>(), vec2<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 6 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr(
+            "12:34 error: no matching constructor for vec4<f32>(vec2<f32>, vec2<f32>, vec2<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Error_TooFewArgumentsVec3) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), vec3<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec4<f32>(vec3<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4_Error_TooManyArgumentsVec3AndScalars) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()), Expr(Source{{12, 40}}, 1_f),
-                         Expr(Source{{12, 46}}, 1_f));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), vec3<f32>(), 1_f, 2_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<f32>(vec3<f32>, f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4_Error_TooManyArgumentsVec3AndVec2) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()),
-                         Construct(Source{{12, 40}}, ty.vec2<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), vec3<f32>(), vec2<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<f32>(vec3<f32>, vec2<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4_Error_TooManyArgumentsVec2AndVec3) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
-                         Construct(Source{{12, 40}}, ty.vec3<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), vec2<f32>(), vec3<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<f32>(vec2<f32>, vec3<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4_Error_TooManyArgumentsVec3AndVec3) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()),
-                         Construct(Source{{12, 40}}, ty.vec3<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), vec3<f32>(), vec3<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 6 component(s)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<f32>(vec3<f32>, vec3<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Error_InvalidArgumentType) {
-    auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.mat2x2<f32>()));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f32>(), mat2x2<f32>()));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: expected vector or scalar type in vector "
-              "constructor; found: mat2x2<f32>");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec4<f32>(mat2x2<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_ZeroValue) {
@@ -1590,13 +1569,13 @@
 
 TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_NestedVectorConstructors_InnerError) {
-    auto* tc = vec4<f32>(
-        vec4<f32>(1_f, 1_f, vec3<f32>(Expr(Source{{12, 34}}, 1_f), Expr(Source{{12, 34}}, 1_f))),
-        1_f);
-    WrapInFunction(tc);
+    WrapInFunction(vec4<f32>(vec4<f32>(1_f, 1_f,  //
+                                       Construct(Source{{12, 34}}, ty.vec3<f32>(), 1_f, 1_f)),
+                             1_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<f32>(f32, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_NestedVectorConstructors_Success) {
@@ -1615,13 +1594,11 @@
     auto* alias = Alias("UnsignedInt", ty.u32());
     Global("uint_var", ty.Of(alias), ast::StorageClass::kPrivate);
 
-    auto* tc = vec2<f32>(Expr(Source{{12, 34}}, "uint_var"));
+    auto* tc = Construct(Source{{12, 34}}, ty.vec2<f32>(), "uint_var");
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'f32', found 'u32'");
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for vec2<f32>(u32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vector_Alias_Argument_Success) {
@@ -1640,13 +1617,11 @@
 
     // vec2<Float32>(1.0f, 1u)
     auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
-    auto* tc = Construct(Source{{12, 34}}, vec_type, 1_f, Expr(Source{{12, 40}}, 1_u));
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, vec_type, 1_f, 1_u));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:40 error: type in vector constructor does not match vector "
-              "type: expected 'f32', found 'u32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<f32>(f32, u32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vector_ElementTypeAlias_Success) {
@@ -1666,13 +1641,11 @@
 
     // vec3<u32>(vec<Float32>(), 1.0f)
     auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
-    auto* tc = vec3<u32>(Construct(Source{{12, 34}}, vec_type), 1_f);
-    WrapInFunction(tc);
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<u32>(), Construct(vec_type), 1_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: type in vector constructor does not match vector "
-              "type: expected 'u32', found 'f32'");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<u32>(vec2<f32>, f32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
@@ -1946,10 +1919,10 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVectorElementTypeWithoutArgs) {
-    WrapInFunction(Construct(create<ast::Vector>(Source{{12, 34}}, nullptr, 3)));
+    WrapInFunction(Construct(Source{{12, 34}}, create<ast::Vector>(nullptr, 3)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for vec3()"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec2ElementTypeFromScalarsMismatch) {
@@ -1958,11 +1931,7 @@
                              Expr(Source{{1, 3}}, 2_u)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
-1:2 note: argument 0 has type i32
-1:3 note: argument 1 has type u32)");
+    EXPECT_THAT(r()->error(), HasSubstr("1:1 error: no matching constructor for vec2(i32, u32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec3ElementTypeFromScalarsMismatch) {
@@ -1972,12 +1941,8 @@
                              Expr(Source{{1, 4}}, 3_i)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
-1:2 note: argument 0 has type i32
-1:3 note: argument 1 has type u32
-1:4 note: argument 2 has type i32)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("1:1 error: no matching constructor for vec3(i32, u32, i32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec3ElementTypeFromScalarAndVec2Mismatch) {
@@ -1986,11 +1951,8 @@
                              Construct(Source{{1, 3}}, ty.vec2<f32>(), 2_f, 3_f)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
-1:2 note: argument 0 has type i32
-1:3 note: argument 1 has type vec2<f32>)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("1:1 error: no matching constructor for vec3(i32, vec2<f32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec4ElementTypeFromScalarsMismatch) {
@@ -2001,13 +1963,8 @@
                              Expr(Source{{1, 5}}, 4_i)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
-1:2 note: argument 0 has type i32
-1:3 note: argument 1 has type i32
-1:4 note: argument 2 has type f32
-1:5 note: argument 3 has type i32)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("1:1 error: no matching constructor for vec4(i32, i32, f32, i32)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec4ElementTypeFromScalarAndVec3Mismatch) {
@@ -2016,11 +1973,8 @@
                              Construct(Source{{1, 3}}, ty.vec3<u32>(), 2_u, 3_u, 4_u)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
-1:2 note: argument 0 has type i32
-1:3 note: argument 1 has type vec3<u32>)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("1:1 error: no matching constructor for vec4(i32, vec3<u32>)"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec4ElementTypeFromVec2AndVec2Mismatch) {
@@ -2029,11 +1983,8 @@
                              Construct(Source{{1, 3}}, ty.vec2<u32>(), 3_u, 4_u)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
-1:2 note: argument 0 has type vec2<i32>
-1:3 note: argument 1 has type vec2<u32>)");
+    EXPECT_THAT(r()->error(),
+                HasSubstr("1:1 error: no matching constructor for vec4(vec2<i32>, vec2<u32>)"));
 }
 
 }  // namespace VectorConstructor
@@ -2058,22 +2009,22 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns - 1; i++) {
+    for (uint32_t i = 0; i < param.columns - 1; i++) {
         auto* vec_type = ty.vec<f32>(param.rows);
-        args.push_back(Construct(Source{{12, i}}, vec_type));
-        if (i > 1) {
+        args.push_back(Construct(vec_type));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "vec" << param.rows << "<f32>";
     }
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_ElementConstructor_Error_TooFewArguments) {
@@ -2083,21 +2034,21 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns * param.rows - 1; i++) {
-        args.push_back(Construct(Source{{12, i}}, ty.f32()));
-        if (i > 1) {
+    for (uint32_t i = 0; i < param.columns * param.rows - 1; i++) {
+        args.push_back(Construct(ty.f32()));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "f32";
     }
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooManyArguments) {
@@ -2107,22 +2058,22 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns + 1; i++) {
+    for (uint32_t i = 0; i < param.columns + 1; i++) {
         auto* vec_type = ty.vec<f32>(param.rows);
-        args.push_back(Construct(Source{{12, i}}, vec_type));
-        if (i > 1) {
+        args.push_back(Construct(vec_type));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "vec" << param.rows << "<f32>";
     }
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_ElementConstructor_Error_TooManyArguments) {
@@ -2132,21 +2083,21 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns * param.rows + 1; i++) {
-        args.push_back(Construct(Source{{12, i}}, ty.f32()));
-        if (i > 1) {
+    for (uint32_t i = 0; i < param.columns * param.rows + 1; i++) {
+        args.push_back(Construct(ty.f32()));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "f32";
     }
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_InvalidArgumentType) {
@@ -2156,22 +2107,22 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns; i++) {
+    for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec<u32>(param.rows);
-        args.push_back(Construct(Source{{12, i}}, vec_type));
-        if (i > 1) {
+        args.push_back(Construct(vec_type));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "vec" << param.rows << "<u32>";
     }
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_ElementConstructor_Error_InvalidArgumentType) {
@@ -2181,21 +2132,21 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns; i++) {
-        args.push_back(Expr(Source{{12, i}}, 1_u));
-        if (i > 1) {
+    for (uint32_t i = 0; i < param.columns; i++) {
+        args.push_back(Expr(1_u));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "u32";
     }
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooFewRowsInVectorArgument) {
@@ -2210,10 +2161,10 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns - 1; i++) {
+    for (uint32_t i = 0; i < param.columns; i++) {
         auto* valid_vec_type = ty.vec<f32>(param.rows);
-        args.push_back(Construct(Source{{12, i}}, valid_vec_type));
-        if (i > 1) {
+        args.push_back(Construct(valid_vec_type));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "vec" << param.rows << "<f32>";
@@ -2224,12 +2175,12 @@
     args_tys << ", vec" << (param.rows - 1) << "<f32>";
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooManyRowsInVectorArgument) {
@@ -2244,26 +2195,25 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns - 1; i++) {
+    for (uint32_t i = 0; i < param.columns; i++) {
         auto* valid_vec_type = ty.vec<f32>(param.rows);
-        args.push_back(Construct(Source{{12, i}}, valid_vec_type));
-        if (i > 1) {
+        args.push_back(Construct(valid_vec_type));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "vec" << param.rows << "<f32>";
     }
-    const size_t kInvalidLoc = 2 * (param.columns - 1);
     auto* invalid_vec_type = ty.vec<f32>(param.rows + 1);
-    args.push_back(Construct(Source{{12, kInvalidLoc}}, invalid_vec_type));
+    args.push_back(Construct(invalid_vec_type));
     args_tys << ", vec" << (param.rows + 1) << "<f32>";
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ZeroValue_Success) {
@@ -2283,13 +2233,13 @@
     const auto param = GetParam();
 
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns; i++) {
+    for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec<f32>(param.rows);
-        args.push_back(Construct(Source{{12, i}}, vec_type));
+        args.push_back(Construct(vec_type));
     }
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(matrix_type, std::move(args));
     WrapInFunction(tc);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -2301,12 +2251,12 @@
     const auto param = GetParam();
 
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns * param.rows; i++) {
-        args.push_back(Construct(Source{{12, i}}, ty.f32()));
+    for (uint32_t i = 0; i < param.columns * param.rows; i++) {
+        args.push_back(Construct(ty.f32()));
     }
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(matrix_type, std::move(args));
     WrapInFunction(tc);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -2320,22 +2270,22 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns; i++) {
+    for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec(ty.u32(), param.rows);
-        args.push_back(Construct(Source{{12, i}}, vec_type));
-        if (i > 1) {
+        args.push_back(Construct(vec_type));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "vec" << param.rows << "<u32>";
     }
 
     auto* matrix_type = ty.mat(ty.Of(f32_alias), param.columns, param.rows);
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ElementTypeAlias_Success) {
@@ -2345,9 +2295,9 @@
     auto* f32_alias = Alias("Float32", ty.f32());
 
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns; i++) {
+    for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec<f32>(param.rows);
-        args.push_back(Construct(Source{{12, i}}, vec_type));
+        args.push_back(Construct(vec_type));
     }
 
     auto* matrix_type = ty.mat(ty.Of(f32_alias), param.columns, param.rows);
@@ -2359,18 +2309,13 @@
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_MatrixConstructor_ArgumentTypeAlias_Error) {
     auto* alias = Alias("VectorUnsigned2", ty.vec2<u32>());
-    auto* tc = mat2x2<f32>(Construct(Source{{12, 34}}, ty.Of(alias)), vec2<f32>());
+    auto* tc = Construct(Source{{12, 34}}, ty.mat2x2<f32>(), Construct(ty.Of(alias)), vec2<f32>());
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(12:34 error: no matching constructor mat2x2<f32>(vec2<u32>, vec2<f32>)
-
-3 candidates available:
-  mat2x2<f32>()
-  mat2x2<f32>(f32,...,f32) // 4 arguments
-  mat2x2<f32>(vec2<f32>, vec2<f32>)
-)");
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for mat2x2<f32>(vec2<u32>, vec2<f32>)"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ArgumentTypeAlias_Success) {
@@ -2380,8 +2325,8 @@
     auto* vec_alias = Alias("VectorFloat2", vec_type);
 
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns; i++) {
-        args.push_back(Construct(Source{{12, i}}, ty.Of(vec_alias)));
+    for (uint32_t i = 0; i < param.columns; i++) {
+        args.push_back(Construct(ty.Of(vec_alias)));
     }
 
     auto* tc = Construct(Source{}, matrix_type, std::move(args));
@@ -2397,21 +2342,21 @@
 
     std::stringstream args_tys;
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns; i++) {
+    for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec(ty.Of(f32_alias), param.rows);
-        args.push_back(Construct(Source{{12, i}}, vec_type));
-        if (i > 1) {
+        args.push_back(Construct(vec_type));
+        if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "vec" << param.rows << "<u32>";
     }
 
-    auto* tc = Construct(Source{}, matrix_type, std::move(args));
+    auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
-                                        "(" + args_tys.str() + ")\n\n3 candidates available:"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for " +
+                                        MatrixStr(param) + "(" + args_tys.str() + ")"));
 }
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ArgumentElementTypeAlias_Success) {
@@ -2419,9 +2364,9 @@
     auto* f32_alias = Alias("Float32", ty.f32());
 
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns; i++) {
+    for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec(ty.Of(f32_alias), param.rows);
-        args.push_back(Construct(Source{{12, i}}, vec_type));
+        args.push_back(Construct(vec_type));
     }
 
     auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
@@ -2435,7 +2380,7 @@
     const auto param = GetParam();
 
     ast::ExpressionList args;
-    for (uint32_t i = 1; i <= param.columns; i++) {
+    for (uint32_t i = 0; i < param.columns; i++) {
         args.push_back(Construct(ty.vec<f32>(param.rows)));
     }
 
@@ -2464,20 +2409,21 @@
     const auto param = GetParam();
 
     std::stringstream err;
-    err << "12:34 error: cannot infer matrix element type, as constructor "
-           "arguments have different types";
+    err << "12:34 error: no matching constructor for mat" << param.columns << "x" << param.rows
+        << "(";
 
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        err << "\n";
-        auto src = Source{{1, 10 + i}};
+        if (i > 0) {
+            err << ", ";
+        }
         if (i == 1) {
             // Odd one out
-            args.push_back(Construct(src, ty.vec<i32>(param.rows)));
-            err << src << " note: argument " << i << " has type vec" << param.rows << "<i32>";
+            args.push_back(Construct(ty.vec<i32>(param.rows)));
+            err << "vec" << param.rows << "<i32>";
         } else {
-            args.push_back(Construct(src, ty.vec<f32>(param.rows)));
-            err << src << " note: argument " << i << " has type vec" << param.rows << "<f32>";
+            args.push_back(Construct(ty.vec<f32>(param.rows)));
+            err << "vec" << param.rows << "<f32>";
         }
     }
 
@@ -2485,33 +2431,37 @@
     WrapInFunction(Construct(Source{{12, 34}}, matrix_type, std::move(args)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), err.str());
+    EXPECT_THAT(r()->error(), HasSubstr(err.str()));
 }
 
 TEST_P(MatrixConstructorTest, CannotInferElementTypeFromScalars_Mismatch) {
     const auto param = GetParam();
 
     std::stringstream err;
-    err << "12:34 error: cannot infer matrix element type, as constructor "
-           "arguments have different types";
+    err << "12:34 error: no matching constructor for mat" << param.columns << "x" << param.rows
+        << "(";
+
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.rows * param.columns; i++) {
-        err << "\n";
-        auto src = Source{{1, 10 + i}};
+        if (i > 0) {
+            err << ", ";
+        }
         if (i == 3) {
-            args.push_back(Expr(src, static_cast<i32>(i)));  // The odd one out
-            err << src << " note: argument " << i << " has type i32";
+            args.push_back(Expr(static_cast<i32>(i)));  // The odd one out
+            err << "i32";
         } else {
-            args.push_back(Expr(src, static_cast<f32>(i)));
-            err << src << " note: argument " << i << " has type f32";
+            args.push_back(Expr(static_cast<f32>(i)));
+            err << "f32";
         }
     }
 
+    err << ")";
+
     auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
     WrapInFunction(Construct(Source{{12, 34}}, matrix_type, std::move(args)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_THAT(r()->error(), err.str());
+    EXPECT_THAT(r()->error(), HasSubstr(err.str()));
 }
 
 INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
@@ -2729,7 +2679,7 @@
     WrapInFunction(CallStmt(Construct(Source{{12, 34}}, ty.f32(), 1_i)));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: type cast evaluated but not used");
+    EXPECT_EQ(r()->error(), "12:34 error: type conversion evaluated but not used");
 }
 
 }  // namespace
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 90e18e1..26b705f 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -662,6 +662,24 @@
     EXPECT_EQ(r()->error(), "error: cannot use builtin 'max' as type");
 }
 
+TEST_F(ResolverTypeValidationTest, F16TypeUsedWithExtension) {
+    // enable f16;
+    // var<private> v : f16;
+    Enable(ast::Extension::kF16);
+
+    Global("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);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "error: f16 used without 'f16' extension enabled");
+}
+
 namespace GetCanonicalTests {
 struct Params {
     builder::ast_type_func_ptr create_ast_type;
@@ -719,19 +737,29 @@
 
 }  // namespace GetCanonicalTests
 
-namespace MultisampledTextureTests {
+namespace SampledTextureTests {
 struct DimensionParams {
     ast::TextureDimension dim;
     bool is_valid;
 };
 
-static constexpr DimensionParams dimension_cases[] = {
-    DimensionParams{ast::TextureDimension::k1d, false},
-    DimensionParams{ast::TextureDimension::k2d, true},
-    DimensionParams{ast::TextureDimension::k2dArray, false},
-    DimensionParams{ast::TextureDimension::k3d, false},
-    DimensionParams{ast::TextureDimension::kCube, false},
-    DimensionParams{ast::TextureDimension::kCubeArray, false}};
+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)});
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
+                         SampledTextureDimensionTest,
+                         testing::Values(  //
+                             DimensionParams{ast::TextureDimension::k1d, true},
+                             DimensionParams{ast::TextureDimension::k2d, true},
+                             DimensionParams{ast::TextureDimension::k2dArray, true},
+                             DimensionParams{ast::TextureDimension::k3d, true},
+                             DimensionParams{ast::TextureDimension::kCube, true},
+                             DimensionParams{ast::TextureDimension::kCubeArray, true}));
 
 using MultisampledTextureDimensionTest = ResolverTestWithParam<DimensionParams>;
 TEST_P(MultisampledTextureDimensionTest, All) {
@@ -748,7 +776,13 @@
 }
 INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
                          MultisampledTextureDimensionTest,
-                         testing::ValuesIn(dimension_cases));
+                         testing::Values(  //
+                             DimensionParams{ast::TextureDimension::k1d, false},
+                             DimensionParams{ast::TextureDimension::k2d, true},
+                             DimensionParams{ast::TextureDimension::k2dArray, false},
+                             DimensionParams{ast::TextureDimension::k3d, false},
+                             DimensionParams{ast::TextureDimension::kCube, false},
+                             DimensionParams{ast::TextureDimension::kCubeArray, false}));
 
 struct TypeParams {
     builder::ast_type_func_ptr type_func;
@@ -778,6 +812,26 @@
     TypeParamsFor<alias<mat3x3<f32>>>(false),
 };
 
+using SampledTextureTypeTest = ResolverTestWithParam<TypeParams>;
+TEST_P(SampledTextureTypeTest, All) {
+    auto& params = GetParam();
+    Global(Source{{12, 34}}, "a",
+           ty.sampled_texture(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();
+    } else {
+        EXPECT_FALSE(r()->Resolve());
+        EXPECT_EQ(r()->error(),
+                  "12:34 error: texture_2d<type>: type must be f32, "
+                  "i32 or u32");
+    }
+}
+INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
+                         SampledTextureTypeTest,
+                         testing::ValuesIn(type_cases));
+
 using MultisampledTextureTypeTest = ResolverTestWithParam<TypeParams>;
 TEST_P(MultisampledTextureTypeTest, All) {
     auto& params = GetParam();
@@ -798,7 +852,7 @@
                          MultisampledTextureTypeTest,
                          testing::ValuesIn(type_cases));
 
-}  // namespace MultisampledTextureTests
+}  // namespace SampledTextureTests
 
 namespace StorageTextureTests {
 struct DimensionParams {
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
new file mode 100644
index 0000000..6188521
--- /dev/null
+++ b/src/tint/resolver/uniformity.cc
@@ -0,0 +1,1563 @@
+// 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/resolver/uniformity.h"
+
+#include <limits>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/resolver/dependency_graph.h"
+#include "src/tint/scope_stack.h"
+#include "src/tint/sem/block_statement.h"
+#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/info.h"
+#include "src/tint/sem/loop_statement.h"
+#include "src/tint/sem/statement.h"
+#include "src/tint/sem/switch_statement.h"
+#include "src/tint/sem/type_constructor.h"
+#include "src/tint/sem/type_conversion.h"
+#include "src/tint/sem/variable.h"
+#include "src/tint/utils/block_allocator.h"
+#include "src/tint/utils/map.h"
+#include "src/tint/utils/unique_vector.h"
+
+// Set to `1` to dump the uniformity graph for each function in graphviz format.
+#define TINT_DUMP_UNIFORMITY_GRAPH 0
+
+namespace tint::resolver {
+
+namespace {
+
+/// CallSiteTag describes the uniformity requirements on the call sites of a function.
+enum CallSiteTag {
+    CallSiteRequiredToBeUniform,
+    CallSiteNoRestriction,
+};
+
+/// FunctionTag describes a functions effects on uniformity.
+enum FunctionTag {
+    SubsequentControlFlowMayBeNonUniform,
+    ReturnValueMayBeNonUniform,
+    NoRestriction,
+};
+
+/// ParameterTag describes the uniformity requirements of values passed to a function parameter.
+enum ParameterTag {
+    ParameterRequiredToBeUniform,
+    ParameterRequiredToBeUniformForSubsequentControlFlow,
+    ParameterRequiredToBeUniformForReturnValue,
+    ParameterNoRestriction,
+};
+
+/// Node represents a node in the graph of control flow and value nodes within the analysis of a
+/// single function.
+struct Node {
+    /// Constructor
+    /// @param a the corresponding AST node
+    explicit Node(const ast::Node* a) : ast(a) {}
+
+#if TINT_DUMP_UNIFORMITY_GRAPH
+    /// The node tag.
+    std::string tag;
+#endif
+
+    /// Type describes the type of the node, which is used to determine additional diagnostic
+    /// information.
+    enum Type {
+        kRegular,
+        kFunctionCallArgument,
+        kFunctionCallPointerArgumentResult,
+        kFunctionCallReturnValue,
+    };
+
+    /// The type of the node.
+    Type type = kRegular;
+
+    /// `true` if this node represents a potential control flow change.
+    bool affects_control_flow = false;
+
+    /// The corresponding AST node, or nullptr.
+    const ast::Node* ast = nullptr;
+
+    /// The function call argument index, if applicable.
+    uint32_t arg_index;
+
+    /// The set of edges from this node to other nodes in the graph.
+    utils::UniqueVector<Node*> edges;
+
+    /// The node that this node was visited from, or nullptr if not visited.
+    Node* visited_from = nullptr;
+
+    /// Add an edge to the `to` node.
+    /// @param to the destination node
+    void AddEdge(Node* to) { edges.add(to); }
+};
+
+/// ParameterInfo holds information about the uniformity requirements and effects for a particular
+/// function parameter.
+struct ParameterInfo {
+    /// The semantic node in corresponds to this parameter.
+    const sem::Parameter* sem;
+    /// The parameter's uniformity requirements.
+    ParameterTag tag = ParameterNoRestriction;
+    /// Will be `true` if this function may cause the contents of this pointer parameter to become
+    /// non-uniform.
+    bool pointer_may_become_non_uniform = false;
+    /// The parameters that are required to be uniform for the contents of this pointer parameter to
+    /// be uniform at function exit.
+    std::vector<const sem::Parameter*> pointer_param_output_sources;
+    /// The node in the graph that corresponds to this parameter's initial value.
+    Node* init_value;
+    /// The node in the graph that corresponds to this parameter's output value (or nullptr).
+    Node* pointer_return_value = nullptr;
+};
+
+/// FunctionInfo holds information about the uniformity requirements and effects for a particular
+/// function, as well as the control flow graph.
+struct FunctionInfo {
+    /// Constructor
+    /// @param func the AST function
+    /// @param builder the program builder
+    FunctionInfo(const ast::Function* func, const ProgramBuilder* builder) {
+        name = builder->Symbols().NameFor(func->symbol);
+        callsite_tag = CallSiteNoRestriction;
+        function_tag = NoRestriction;
+
+        // Create special nodes.
+        required_to_be_uniform = CreateNode("RequiredToBeUniform");
+        may_be_non_uniform = CreateNode("MayBeNonUniform");
+        cf_start = CreateNode("CF_start");
+        cf_return = CreateNode("CF_return");
+        if (func->return_type) {
+            value_return = CreateNode("Value_return");
+        }
+
+        // Create nodes for parameters.
+        parameters.resize(func->params.size());
+        for (size_t i = 0; i < func->params.size(); i++) {
+            auto* param = func->params[i];
+            auto param_name = builder->Symbols().NameFor(param->symbol);
+            auto* sem = builder->Sem().Get<sem::Parameter>(param);
+            parameters[i].sem = sem;
+
+            Node* node_init;
+            if (sem->Type()->Is<sem::Pointer>()) {
+                node_init = CreateNode("ptrparam_" + name + "_init");
+                parameters[i].pointer_return_value = CreateNode("ptrparam_" + name + "_return");
+                local_var_decls.insert(sem);
+            } else {
+                node_init = CreateNode("param_" + name);
+            }
+            parameters[i].init_value = node_init;
+            variables.Set(sem, node_init);
+        }
+    }
+
+    /// The name of the function.
+    std::string name;
+
+    /// The call site uniformity requirements.
+    CallSiteTag callsite_tag;
+    /// The function's uniformity effects.
+    FunctionTag function_tag;
+    /// The uniformity requirements of the function's parameters.
+    std::vector<ParameterInfo> parameters;
+
+    /// The control flow graph.
+    utils::BlockAllocator<Node> nodes;
+
+    /// Special `RequiredToBeUniform` node.
+    Node* required_to_be_uniform;
+    /// Special `MayBeNonUniform` node.
+    Node* may_be_non_uniform;
+    /// Special `CF_start` node.
+    Node* cf_start;
+    /// Special `CF_return` node.
+    Node* cf_return;
+    /// Special `Value_return` node.
+    Node* value_return;
+
+    /// Map from variables to their value nodes in the graph, scoped with respect to control flow.
+    ScopeStack<const sem::Variable*, Node*> variables;
+
+    /// The set of a local read-write vars that are in scope at any given point in the process.
+    /// Includes pointer parameters.
+    std::unordered_set<const sem::Variable*> local_var_decls;
+
+    /// LoopSwitchInfo tracks information about the value of variables for a control flow construct.
+    struct LoopSwitchInfo {
+        /// The type of this control flow construct.
+        std::string type;
+        /// The input values for local variables at the start of this construct.
+        std::unordered_map<const sem::Variable*, Node*> var_in_nodes;
+        /// The exit values for local variables at the end of this construct.
+        std::unordered_map<const sem::Variable*, Node*> var_exit_nodes;
+    };
+
+    /// Map from control flow statements to the corresponding LoopSwitchInfo structure.
+    std::unordered_map<const sem::Statement*, LoopSwitchInfo> loop_switch_infos;
+
+    /// Create a new node.
+    /// @param tag a tag used to identify the node for debugging purposes
+    /// @param ast the optional AST node that this node corresponds to
+    /// @returns the new node
+    Node* CreateNode([[maybe_unused]] std::string tag, const ast::Node* ast = nullptr) {
+        auto* node = nodes.Create(ast);
+
+#if TINT_DUMP_UNIFORMITY_GRAPH
+        // Make the tag unique and set it.
+        // This only matters if we're dumping the graph.
+        std::string unique_tag = tag;
+        int suffix = 0;
+        while (tags_.count(unique_tag)) {
+            unique_tag = tag + "_$" + std::to_string(++suffix);
+        }
+        tags_.insert(unique_tag);
+        node->tag = name + "." + unique_tag;
+#endif
+
+        return node;
+    }
+
+    /// Reset the visited status of every node in the graph.
+    void ResetVisited() {
+        for (auto* node : nodes.Objects()) {
+            node->visited_from = nullptr;
+        }
+    }
+
+  private:
+    /// A list of tags that have already been used within the current function.
+    std::unordered_set<std::string> tags_;
+};
+
+/// UniformityGraph is used to analyze the uniformity requirements and effects of functions in a
+/// module.
+class UniformityGraph {
+  public:
+    /// Constructor.
+    /// @param builder the program to analyze
+    explicit UniformityGraph(ProgramBuilder* builder)
+        : builder_(builder), sem_(builder->Sem()), diagnostics_(builder->Diagnostics()) {}
+
+    /// Destructor.
+    ~UniformityGraph() {}
+
+    /// Build and analyze the graph to determine whether the program satisfies the uniformity
+    /// constraints of WGSL.
+    /// @param dependency_graph the dependency-ordered module-scope declarations
+    /// @returns true if all uniformity constraints are satisfied, otherise false
+    bool Build(const DependencyGraph& dependency_graph) {
+#if TINT_DUMP_UNIFORMITY_GRAPH
+        std::cout << "digraph G {\n";
+        std::cout << "rankdir=BT\n";
+#endif
+
+        // Process all functions in the module.
+        bool success = true;
+        for (auto* decl : dependency_graph.ordered_globals) {
+            if (auto* func = decl->As<ast::Function>()) {
+                if (!ProcessFunction(func)) {
+                    success = false;
+                    break;
+                }
+            }
+        }
+
+#if TINT_DUMP_UNIFORMITY_GRAPH
+        std::cout << "\n}\n";
+#endif
+
+        return success;
+    }
+
+  private:
+    const ProgramBuilder* builder_;
+    const sem::Info& sem_;
+    diag::List& diagnostics_;
+
+    /// Map of analyzed function results.
+    std::unordered_map<const ast::Function*, FunctionInfo> functions_;
+
+    /// The function currently being analyzed.
+    FunctionInfo* current_function_;
+
+    /// Create a new node.
+    /// @param tag a tag used to identify the node for debugging purposes.
+    /// @param ast the optional AST node that this node corresponds to
+    /// @returns the new node
+    Node* CreateNode(std::string tag, const ast::Node* ast = nullptr) {
+        return current_function_->CreateNode(tag, ast);
+    }
+
+    /// Process a function.
+    /// @param func the function to process
+    /// @returns true if there are no uniformity issues, false otherwise
+    bool ProcessFunction(const ast::Function* func) {
+        functions_.emplace(func, FunctionInfo(func, builder_));
+        current_function_ = &functions_.at(func);
+
+        // Process function body.
+        if (func->body) {
+            auto* cf = ProcessStatement(current_function_->cf_start, func->body);
+            current_function_->cf_return->AddEdge(cf);
+        }
+
+#if TINT_DUMP_UNIFORMITY_GRAPH
+        // Dump the graph for this function as a subgraph.
+        std::cout << "\nsubgraph cluster_" << current_function_->name << " {\n";
+        std::cout << "  label=" << current_function_->name << ";";
+        for (auto* node : current_function_->nodes.Objects()) {
+            std::cout << "\n  \"" << node->tag << "\";";
+            for (auto* edge : node->edges) {
+                std::cout << "\n  \"" << node->tag << "\" -> \"" << edge->tag << "\";";
+            }
+        }
+        std::cout << "\n}\n";
+#endif
+
+        // Look at which nodes are reachable from "RequiredToBeUniform".
+        {
+            utils::UniqueVector<Node*> reachable;
+            Traverse(current_function_->required_to_be_uniform, &reachable);
+            if (reachable.contains(current_function_->may_be_non_uniform)) {
+                MakeError(*current_function_, current_function_->may_be_non_uniform);
+                return false;
+            }
+            if (reachable.contains(current_function_->cf_start)) {
+                current_function_->callsite_tag = CallSiteRequiredToBeUniform;
+            }
+
+            // Set the parameter tag to ParameterRequiredToBeUniform for each parameter node that
+            // was reachable.
+            for (size_t i = 0; i < func->params.size(); i++) {
+                auto* param = func->params[i];
+                if (reachable.contains(current_function_->variables.Get(sem_.Get(param)))) {
+                    current_function_->parameters[i].tag = ParameterRequiredToBeUniform;
+                }
+            }
+        }
+
+        // Look at which nodes are reachable from "CF_return"
+        {
+            utils::UniqueVector<Node*> reachable;
+            Traverse(current_function_->cf_return, &reachable);
+            if (reachable.contains(current_function_->may_be_non_uniform)) {
+                current_function_->function_tag = SubsequentControlFlowMayBeNonUniform;
+            }
+
+            // Set the parameter tag to ParameterRequiredToBeUniformForSubsequentControlFlow for
+            // each parameter node that was reachable.
+            for (size_t i = 0; i < func->params.size(); i++) {
+                auto* param = func->params[i];
+                if (reachable.contains(current_function_->variables.Get(sem_.Get(param)))) {
+                    current_function_->parameters[i].tag =
+                        ParameterRequiredToBeUniformForSubsequentControlFlow;
+                }
+            }
+        }
+
+        // If "Value_return" exists, look at which nodes are reachable from it
+        if (current_function_->value_return) {
+            utils::UniqueVector<Node*> reachable;
+            Traverse(current_function_->value_return, &reachable);
+            if (reachable.contains(current_function_->may_be_non_uniform)) {
+                current_function_->function_tag = ReturnValueMayBeNonUniform;
+            }
+
+            // Set the parameter tag to ParameterRequiredToBeUniformForReturnValue for each
+            // parameter node that was reachable.
+            for (size_t i = 0; i < func->params.size(); i++) {
+                auto* param = func->params[i];
+                if (reachable.contains(current_function_->variables.Get(sem_.Get(param)))) {
+                    current_function_->parameters[i].tag =
+                        ParameterRequiredToBeUniformForReturnValue;
+                }
+            }
+        }
+
+        // Traverse the graph for each pointer parameter.
+        for (size_t i = 0; i < func->params.size(); i++) {
+            if (current_function_->parameters[i].pointer_return_value == nullptr) {
+                continue;
+            }
+
+            // Reset "visited" state for all nodes.
+            current_function_->ResetVisited();
+
+            utils::UniqueVector<Node*> reachable;
+            Traverse(current_function_->parameters[i].pointer_return_value, &reachable);
+            if (reachable.contains(current_function_->may_be_non_uniform)) {
+                current_function_->parameters[i].pointer_may_become_non_uniform = true;
+            }
+
+            // Check every other parameter to see if they feed into this parameter's final value.
+            for (size_t j = 0; j < func->params.size(); j++) {
+                auto* param_source = sem_.Get<sem::Parameter>(func->params[j]);
+                if (reachable.contains(current_function_->parameters[j].init_value)) {
+                    current_function_->parameters[i].pointer_param_output_sources.push_back(
+                        param_source);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /// Process a statement, returning the new control flow node.
+    /// @param cf the input control flow node
+    /// @param stmt the statement to process d
+    /// @returns the new control flow node
+    Node* ProcessStatement(Node* cf, const ast::Statement* stmt) {
+        return Switch(
+            stmt,
+
+            [&](const ast::AssignmentStatement* a) {
+                auto [cf1, v1] = ProcessExpression(cf, a->rhs);
+                if (a->lhs->Is<ast::PhonyExpression>()) {
+                    return cf1;
+                } else {
+                    auto [cf2, l2] = ProcessLValueExpression(cf1, a->lhs);
+                    l2->AddEdge(v1);
+                    return cf2;
+                }
+            },
+
+            [&](const ast::BlockStatement* b) {
+                std::unordered_map<const sem::Variable*, Node*> scoped_assignments;
+                {
+                    // Push a new scope for variable assignments in the block.
+                    current_function_->variables.Push();
+                    TINT_DEFER(current_function_->variables.Pop());
+
+                    for (auto* s : b->statements) {
+                        cf = ProcessStatement(cf, s);
+                        if (!sem_.Get(s)->Behaviors().Contains(sem::Behavior::kNext)) {
+                            break;
+                        }
+                    }
+
+                    if (sem_.Get<sem::FunctionBlockStatement>(b)) {
+                        // We've reached the end of the function body.
+                        // Add edges from pointer parameter outputs to their current value.
+                        for (auto param : current_function_->parameters) {
+                            if (param.pointer_return_value) {
+                                param.pointer_return_value->AddEdge(
+                                    current_function_->variables.Get(param.sem));
+                            }
+                        }
+                    }
+
+                    scoped_assignments = std::move(current_function_->variables.Top());
+                }
+
+                // Propagate all variables assignments to the containing scope if the behavior is
+                // either 'Next' or 'Fallthrough'.
+                auto& behaviors = sem_.Get(b)->Behaviors();
+                if (behaviors.Contains(sem::Behavior::kNext) ||
+                    behaviors.Contains(sem::Behavior::kFallthrough)) {
+                    for (auto var : scoped_assignments) {
+                        current_function_->variables.Set(var.first, var.second);
+                    }
+                }
+
+                // Remove any variables declared in this scope from the set of in-scope variables.
+                for (auto* d : sem_.Get<sem::BlockStatement>(b)->Decls()) {
+                    current_function_->local_var_decls.erase(sem_.Get<sem::LocalVariable>(d));
+                }
+
+                return cf;
+            },
+
+            [&](const ast::BreakStatement* b) {
+                // Find the loop or switch statement that we are in.
+                auto* parent = sem_.Get(b)
+                                   ->FindFirstParent<sem::SwitchStatement, sem::LoopStatement,
+                                                     sem::ForLoopStatement>();
+                TINT_ASSERT(Resolver, current_function_->loop_switch_infos.count(parent));
+                auto& info = current_function_->loop_switch_infos.at(parent);
+
+                // Propagate variable values to the loop/switch exit nodes.
+                for (auto* var : current_function_->local_var_decls) {
+                    // Skip variables that were declared inside this loop/switch.
+                    if (auto* lv = var->As<sem::LocalVariable>();
+                        lv &&
+                        lv->Statement()->FindFirstParent([&](auto* s) { return s == parent; })) {
+                        continue;
+                    }
+
+                    // Add an edge from the variable exit node to its value at this point.
+                    auto* exit_node = utils::GetOrCreate(info.var_exit_nodes, var, [&]() {
+                        auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
+                        return CreateNode(name + "_value_" + info.type + "_exit");
+                    });
+                    exit_node->AddEdge(current_function_->variables.Get(var));
+                }
+
+                return cf;
+            },
+
+            [&](const ast::CallStatement* c) {
+                auto [cf1, _] = ProcessCall(cf, c->expr);
+                return cf1;
+            },
+
+            [&](const ast::CompoundAssignmentStatement* c) {
+                // The compound assignment statement `a += b` is equivalent to `a = a + b`.
+                auto [cf1, v1] = ProcessExpression(cf, c->lhs);
+                auto [cf2, v2] = ProcessExpression(cf1, c->rhs);
+                auto* result = CreateNode("binary_expr_result");
+                result->AddEdge(v1);
+                result->AddEdge(v2);
+
+                auto [cf3, l3] = ProcessLValueExpression(cf2, c->lhs);
+                l3->AddEdge(result);
+                return cf3;
+            },
+
+            [&](const ast::ContinueStatement* c) {
+                // Find the loop statement that we are in.
+                auto* parent =
+                    sem_.Get(c)->FindFirstParent<sem::LoopStatement, sem::ForLoopStatement>();
+                TINT_ASSERT(Resolver, current_function_->loop_switch_infos.count(parent));
+                auto& info = current_function_->loop_switch_infos.at(parent);
+
+                // Propagate assignments to the loop input nodes.
+                for (auto* var : current_function_->local_var_decls) {
+                    // Skip variables that were declared inside this loop.
+                    if (auto* lv = var->As<sem::LocalVariable>();
+                        lv &&
+                        lv->Statement()->FindFirstParent([&](auto* s) { return s == parent; })) {
+                        continue;
+                    }
+
+                    // Add an edge from the variable's loop input node to its value at this point.
+                    TINT_ASSERT(Resolver, info.var_in_nodes.count(var));
+                    auto* in_node = info.var_in_nodes.at(var);
+                    auto* out_node = current_function_->variables.Get(var);
+                    if (out_node != in_node) {
+                        in_node->AddEdge(out_node);
+                    }
+                }
+                return cf;
+            },
+
+            [&](const ast::DiscardStatement*) { return cf; },
+
+            [&](const ast::FallthroughStatement*) { return cf; },
+
+            [&](const ast::ForLoopStatement* f) {
+                auto* sem_loop = sem_.Get(f);
+                auto* cfx = CreateNode("loop_start");
+
+                // Insert the initializer before the loop.
+                auto* cf_init = cf;
+                if (f->initializer) {
+                    cf_init = ProcessStatement(cf, f->initializer);
+                }
+                auto* cf_start = cf_init;
+
+                auto& info = current_function_->loop_switch_infos[sem_loop];
+                info.type = "forloop";
+
+                // Create input nodes for any variables declared before this loop.
+                for (auto* v : current_function_->local_var_decls) {
+                    auto name = builder_->Symbols().NameFor(v->Declaration()->symbol);
+                    auto* in_node = CreateNode(name + "_value_forloop_in");
+                    in_node->AddEdge(current_function_->variables.Get(v));
+                    info.var_in_nodes[v] = in_node;
+                    current_function_->variables.Set(v, in_node);
+                }
+
+                // Insert the condition at the start of the loop body.
+                if (f->condition) {
+                    auto [cf_cond, v] = ProcessExpression(cfx, f->condition);
+                    auto* cf_condition_end = CreateNode("for_condition_CFend", f);
+                    cf_condition_end->affects_control_flow = true;
+                    cf_condition_end->AddEdge(v);
+                    cf_start = cf_condition_end;
+
+                    // Propagate assignments to the loop exit nodes.
+                    for (auto* var : current_function_->local_var_decls) {
+                        auto* exit_node = utils::GetOrCreate(info.var_exit_nodes, var, [&]() {
+                            auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
+                            return CreateNode(name + "_value_" + info.type + "_exit");
+                        });
+                        exit_node->AddEdge(current_function_->variables.Get(var));
+                    }
+                }
+                auto* cf1 = ProcessStatement(cf_start, f->body);
+
+                // Insert the continuing statement at the end of the loop body.
+                if (f->continuing) {
+                    auto* cf2 = ProcessStatement(cf1, f->continuing);
+                    cfx->AddEdge(cf2);
+                } else {
+                    cfx->AddEdge(cf1);
+                }
+                cfx->AddEdge(cf);
+
+                // Add edges from variable loop input nodes to their values at the end of the loop.
+                for (auto v : info.var_in_nodes) {
+                    auto* in_node = v.second;
+                    auto* out_node = current_function_->variables.Get(v.first);
+                    if (out_node != in_node) {
+                        in_node->AddEdge(out_node);
+                    }
+                }
+
+                // Set each variable's exit node as its value in the outer scope.
+                for (auto v : info.var_exit_nodes) {
+                    current_function_->variables.Set(v.first, v.second);
+                }
+
+                current_function_->loop_switch_infos.erase(sem_loop);
+
+                if (sem_loop->Behaviors() == sem::Behaviors{sem::Behavior::kNext}) {
+                    return cf;
+                } else {
+                    return cfx;
+                }
+            },
+
+            [&](const ast::IfStatement* i) {
+                auto* sem_if = sem_.Get(i);
+                auto [_, v_cond] = ProcessExpression(cf, i->condition);
+
+                // Add a diagnostic node to capture the control flow change.
+                auto* v = current_function_->CreateNode("if_stmt", i);
+                v->affects_control_flow = true;
+                v->AddEdge(v_cond);
+
+                std::unordered_map<const sem::Variable*, Node*> true_vars;
+                std::unordered_map<const sem::Variable*, Node*> false_vars;
+
+                // Helper to process a statement with a new scope for variable assignments.
+                // Populates `assigned_vars` with new nodes for any variables that are assigned in
+                // this statement.
+                auto process_in_scope =
+                    [&](Node* cf_in, const ast::Statement* s,
+                        std::unordered_map<const sem::Variable*, Node*>& assigned_vars) {
+                        // Push a new scope for variable assignments.
+                        current_function_->variables.Push();
+
+                        // Process the statement.
+                        auto* cf_out = ProcessStatement(cf_in, s);
+
+                        assigned_vars = current_function_->variables.Top();
+
+                        // Pop the scope and return.
+                        current_function_->variables.Pop();
+                        return cf_out;
+                    };
+
+                auto* cf1 = process_in_scope(v, i->body, true_vars);
+
+                bool true_has_next = sem_.Get(i->body)->Behaviors().Contains(sem::Behavior::kNext);
+                bool false_has_next = true;
+
+                Node* cf2 = nullptr;
+                if (i->else_statement) {
+                    cf2 = process_in_scope(v, i->else_statement, false_vars);
+
+                    false_has_next =
+                        sem_.Get(i->else_statement)->Behaviors().Contains(sem::Behavior::kNext);
+                }
+
+                // Update values for any variables assigned in the if or else blocks.
+                for (auto* var : current_function_->local_var_decls) {
+                    // Skip variables not assigned in either block.
+                    if (true_vars.count(var) == 0 && false_vars.count(var) == 0) {
+                        continue;
+                    }
+
+                    // Create an exit node for the variable.
+                    auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
+                    auto* out_node = CreateNode(name + "_value_if_exit");
+
+                    // Add edges to the assigned value or the initial value.
+                    // Only add edges if the behavior for that block contains 'Next'.
+                    if (true_has_next) {
+                        if (true_vars.count(var)) {
+                            out_node->AddEdge(true_vars.at(var));
+                        } else {
+                            out_node->AddEdge(current_function_->variables.Get(var));
+                        }
+                    }
+                    if (false_has_next) {
+                        if (false_vars.count(var)) {
+                            out_node->AddEdge(false_vars.at(var));
+                        } else {
+                            out_node->AddEdge(current_function_->variables.Get(var));
+                        }
+                    }
+
+                    current_function_->variables.Set(var, out_node);
+                }
+
+                if (sem_if->Behaviors() != sem::Behaviors{sem::Behavior::kNext}) {
+                    auto* cf_end = CreateNode("if_CFend");
+                    cf_end->AddEdge(cf1);
+                    if (cf2) {
+                        cf_end->AddEdge(cf2);
+                    }
+                    return cf_end;
+                }
+                return cf;
+            },
+
+            [&](const ast::IncrementDecrementStatement* i) {
+                // The increment/decrement statement `i++` is equivalent to `i = i + 1`.
+                auto [cf1, v1] = ProcessExpression(cf, i->lhs);
+                auto* result = CreateNode("incdec_result");
+                result->AddEdge(v1);
+                result->AddEdge(cf1);
+
+                auto [cf2, l2] = ProcessLValueExpression(cf1, i->lhs);
+                l2->AddEdge(result);
+                return cf2;
+            },
+
+            [&](const ast::LoopStatement* l) {
+                auto* sem_loop = sem_.Get(l);
+                auto* cfx = CreateNode("loop_start");
+
+                auto& info = current_function_->loop_switch_infos[sem_loop];
+                info.type = "loop";
+
+                // Create input nodes for any variables declared before this loop.
+                for (auto* v : current_function_->local_var_decls) {
+                    auto name = builder_->Symbols().NameFor(v->Declaration()->symbol);
+                    auto* in_node = CreateNode(name + "_value_loop_in");
+                    in_node->AddEdge(current_function_->variables.Get(v));
+                    info.var_in_nodes[v] = in_node;
+                    current_function_->variables.Set(v, in_node);
+                }
+
+                auto* cf1 = ProcessStatement(cfx, l->body);
+                if (l->continuing) {
+                    auto* cf2 = ProcessStatement(cf1, l->continuing);
+                    cfx->AddEdge(cf2);
+                } else {
+                    cfx->AddEdge(cf1);
+                }
+                cfx->AddEdge(cf);
+
+                // Add edges from variable loop input nodes to their values at the end of the loop.
+                for (auto v : info.var_in_nodes) {
+                    auto* in_node = v.second;
+                    auto* out_node = current_function_->variables.Get(v.first);
+                    if (out_node != in_node) {
+                        in_node->AddEdge(out_node);
+                    }
+                }
+
+                // Set each variable's exit node as its value in the outer scope.
+                for (auto v : info.var_exit_nodes) {
+                    current_function_->variables.Set(v.first, v.second);
+                }
+
+                current_function_->loop_switch_infos.erase(sem_loop);
+
+                if (sem_loop->Behaviors() == sem::Behaviors{sem::Behavior::kNext}) {
+                    return cf;
+                } else {
+                    return cfx;
+                }
+            },
+            [&](const ast::ReturnStatement* r) {
+                Node* cf_ret;
+                if (r->value) {
+                    auto [cf1, v] = ProcessExpression(cf, r->value);
+                    current_function_->cf_return->AddEdge(cf1);
+                    current_function_->value_return->AddEdge(v);
+                    cf_ret = cf1;
+                } else {
+                    TINT_ASSERT(Resolver, cf != nullptr);
+                    current_function_->cf_return->AddEdge(cf);
+                    cf_ret = cf;
+                }
+
+                // Add edges from each pointer parameter output to its current value.
+                for (auto param : current_function_->parameters) {
+                    if (param.pointer_return_value) {
+                        param.pointer_return_value->AddEdge(
+                            current_function_->variables.Get(param.sem));
+                    }
+                }
+
+                return cf_ret;
+            },
+            [&](const ast::SwitchStatement* s) {
+                auto* sem_switch = sem_.Get(s);
+                auto [cfx, v_cond] = ProcessExpression(cf, s->condition);
+
+                // Add a diagnostic node to capture the control flow change.
+                auto* v = current_function_->CreateNode("switch_stmt", s);
+                v->affects_control_flow = true;
+                v->AddEdge(v_cond);
+
+                Node* cf_end = nullptr;
+                if (sem_switch->Behaviors() != sem::Behaviors{sem::Behavior::kNext}) {
+                    cf_end = CreateNode("switch_CFend");
+                }
+
+                auto& info = current_function_->loop_switch_infos[sem_switch];
+                info.type = "switch";
+
+                auto* cf_n = v;
+                bool previous_case_has_fallthrough = false;
+                for (auto* c : s->body) {
+                    auto* sem_case = sem_.Get(c);
+
+                    if (previous_case_has_fallthrough) {
+                        cf_n = ProcessStatement(cf_n, c->body);
+                    } else {
+                        current_function_->variables.Push();
+                        cf_n = ProcessStatement(v, c->body);
+                    }
+
+                    if (cf_end) {
+                        cf_end->AddEdge(cf_n);
+                    }
+
+                    bool has_fallthrough =
+                        sem_case->Behaviors().Contains(sem::Behavior::kFallthrough);
+                    if (!has_fallthrough) {
+                        if (sem_case->Behaviors().Contains(sem::Behavior::kNext)) {
+                            // Propagate variable values to the switch exit nodes.
+                            for (auto* var : current_function_->local_var_decls) {
+                                // Skip variables that were declared inside the switch.
+                                if (auto* lv = var->As<sem::LocalVariable>();
+                                    lv && lv->Statement()->FindFirstParent(
+                                              [&](auto* st) { return st == sem_switch; })) {
+                                    continue;
+                                }
+
+                                // Add an edge from the variable exit node to its new value.
+                                auto* exit_node =
+                                    utils::GetOrCreate(info.var_exit_nodes, var, [&]() {
+                                        auto name =
+                                            builder_->Symbols().NameFor(var->Declaration()->symbol);
+                                        return CreateNode(name + "_value_" + info.type + "_exit");
+                                    });
+                                exit_node->AddEdge(current_function_->variables.Get(var));
+                            }
+                        }
+                        current_function_->variables.Pop();
+                    }
+                    previous_case_has_fallthrough = has_fallthrough;
+                }
+
+                // Update nodes for any variables assigned in the switch statement.
+                for (auto var : info.var_exit_nodes) {
+                    current_function_->variables.Set(var.first, var.second);
+                }
+
+                return cf_end ? cf_end : cf;
+            },
+            [&](const ast::VariableDeclStatement* decl) {
+                Node* node;
+                if (decl->variable->constructor) {
+                    auto [cf1, v] = ProcessExpression(cf, decl->variable->constructor);
+                    cf = cf1;
+                    node = v;
+                } else {
+                    node = cf;
+                }
+                current_function_->variables.Set(sem_.Get(decl->variable), node);
+
+                if (!decl->variable->is_const) {
+                    current_function_->local_var_decls.insert(
+                        sem_.Get<sem::LocalVariable>(decl->variable));
+                }
+
+                return cf;
+            },
+            [&](Default) {
+                TINT_ICE(Resolver, diagnostics_)
+                    << "unknown statement type: " << std::string(stmt->TypeInfo().name);
+                return nullptr;
+            });
+    }
+
+    /// Process an identifier expression.
+    /// @param cf the input control flow node
+    /// @param ident the identifier expression to process
+    /// @returns a pair of (control flow node, value node)
+    std::pair<Node*, Node*> ProcessIdentExpression(Node* cf,
+                                                   const ast::IdentifierExpression* ident) {
+        // Helper to check if the entry point attribute of `obj` indicates non-uniformity.
+        auto has_nonuniform_entry_point_attribute = [](auto* obj) {
+            // Only the num_workgroups and workgroup_id builtins are uniform.
+            if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(obj->attributes)) {
+                if (builtin->builtin == ast::Builtin::kNumWorkgroups ||
+                    builtin->builtin == ast::Builtin::kWorkgroupId) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        auto name = builder_->Symbols().NameFor(ident->symbol);
+        auto* sem = sem_.Get<sem::VariableUser>(ident)->Variable();
+        auto* node = CreateNode(name + "_ident_expr", ident);
+        return Switch(
+            sem,
+
+            [&](const sem::Parameter* param) {
+                auto* user_func = param->Owner()->As<sem::Function>();
+                if (user_func && user_func->Declaration()->IsEntryPoint()) {
+                    if (auto* str = param->Type()->As<sem::Struct>()) {
+                        // We consider the whole struct to be non-uniform if any one of its members
+                        // is non-uniform.
+                        bool uniform = true;
+                        for (auto* member : str->Members()) {
+                            if (has_nonuniform_entry_point_attribute(member->Declaration())) {
+                                uniform = false;
+                            }
+                        }
+                        node->AddEdge(uniform ? cf : current_function_->may_be_non_uniform);
+                        return std::make_pair(cf, node);
+                    } else {
+                        if (has_nonuniform_entry_point_attribute(param->Declaration())) {
+                            node->AddEdge(current_function_->may_be_non_uniform);
+                        } else {
+                            node->AddEdge(cf);
+                        }
+                        return std::make_pair(cf, node);
+                    }
+                } else {
+                    auto* x = current_function_->variables.Get(param);
+                    node->AddEdge(cf);
+                    node->AddEdge(x);
+                    return std::make_pair(cf, node);
+                }
+            },
+
+            [&](const sem::GlobalVariable* global) {
+                if (global->Declaration()->is_const || global->Access() == ast::Access::kRead) {
+                    node->AddEdge(cf);
+                } else {
+                    node->AddEdge(current_function_->may_be_non_uniform);
+                }
+                return std::make_pair(cf, node);
+            },
+
+            [&](const sem::LocalVariable* local) {
+                node->AddEdge(cf);
+                if (auto* x = current_function_->variables.Get(local)) {
+                    node->AddEdge(x);
+                }
+                return std::make_pair(cf, node);
+            },
+
+            [&](Default) {
+                TINT_ICE(Resolver, diagnostics_)
+                    << "unknown identifier expression type: " << std::string(sem->TypeInfo().name);
+                return std::pair<Node*, Node*>(nullptr, nullptr);
+            });
+    }
+
+    /// Process an expression.
+    /// @param cf the input control flow node
+    /// @param expr the expression to process
+    /// @returns a pair of (control flow node, value node)
+    std::pair<Node*, Node*> ProcessExpression(Node* cf, const ast::Expression* expr) {
+        return Switch(
+            expr,
+
+            [&](const ast::BinaryExpression* b) {
+                if (b->IsLogical()) {
+                    // Short-circuiting binary operators are a special case.
+                    auto [cf1, v1] = ProcessExpression(cf, b->lhs);
+
+                    // Add a diagnostic node to capture the control flow change.
+                    auto* v1_cf = current_function_->CreateNode("short_circuit_op", b);
+                    v1_cf->affects_control_flow = true;
+                    v1_cf->AddEdge(v1);
+
+                    auto [cf2, v2] = ProcessExpression(v1_cf, b->rhs);
+                    return std::pair<Node*, Node*>(cf2, v2);
+                } else {
+                    auto [cf1, v1] = ProcessExpression(cf, b->lhs);
+                    auto [cf2, v2] = ProcessExpression(cf1, b->rhs);
+                    auto* result = CreateNode("binary_expr_result");
+                    result->AddEdge(v1);
+                    result->AddEdge(v2);
+                    return std::pair<Node*, Node*>(cf2, result);
+                }
+            },
+
+            [&](const ast::BitcastExpression* b) { return ProcessExpression(cf, b->expr); },
+
+            [&](const ast::CallExpression* c) { return ProcessCall(cf, c); },
+
+            [&](const ast::IdentifierExpression* i) { return ProcessIdentExpression(cf, i); },
+
+            [&](const ast::IndexAccessorExpression* i) {
+                auto [cf1, v1] = ProcessExpression(cf, i->object);
+                auto [cf2, v2] = ProcessExpression(cf1, i->index);
+                auto* result = CreateNode("index_accessor_result");
+                result->AddEdge(v1);
+                result->AddEdge(v2);
+                return std::pair<Node*, Node*>(cf2, result);
+            },
+
+            [&](const ast::LiteralExpression*) { return std::make_pair(cf, cf); },
+
+            [&](const ast::MemberAccessorExpression* m) {
+                return ProcessExpression(cf, m->structure);
+            },
+
+            [&](const ast::UnaryOpExpression* u) {
+                if (u->op == ast::UnaryOp::kIndirection) {
+                    // Cut the analysis short, since we only need to know the originating variable
+                    // which is being accessed.
+                    auto* source_var = sem_.Get(u)->SourceVariable();
+                    auto* value = current_function_->variables.Get(source_var);
+                    if (!value) {
+                        value = cf;
+                    }
+                    return std::pair<Node*, Node*>(cf, value);
+                }
+                return ProcessExpression(cf, u->expr);
+            },
+
+            [&](Default) {
+                TINT_ICE(Resolver, diagnostics_)
+                    << "unknown expression type: " << std::string(expr->TypeInfo().name);
+                return std::pair<Node*, Node*>(nullptr, nullptr);
+            });
+    }
+
+    /// Process an LValue expression.
+    /// @param cf the input control flow node
+    /// @param expr the expression to process
+    /// @returns a pair of (control flow node, variable node)
+    std::pair<Node*, Node*> ProcessLValueExpression(Node* cf, const ast::Expression* expr) {
+        return Switch(
+            expr,
+
+            [&](const ast::IdentifierExpression* i) {
+                auto name = builder_->Symbols().NameFor(i->symbol);
+                auto* sem = sem_.Get<sem::VariableUser>(i);
+                if (sem->Variable()->Is<sem::GlobalVariable>()) {
+                    return std::make_pair(cf, current_function_->may_be_non_uniform);
+                } else if (auto* local = sem->Variable()->As<sem::LocalVariable>()) {
+                    // Create a new value node for this variable.
+                    auto* value = CreateNode(name + "_lvalue");
+                    auto* old_value = current_function_->variables.Set(local, value);
+
+                    // Aggregate values link back to their previous value, as they can never become
+                    // uniform again.
+                    if (!local->Type()->UnwrapRef()->is_scalar() && old_value) {
+                        value->AddEdge(old_value);
+                    }
+
+                    return std::make_pair(cf, value);
+                } else {
+                    TINT_ICE(Resolver, diagnostics_)
+                        << "unknown lvalue identifier expression type: "
+                        << std::string(sem->Variable()->TypeInfo().name);
+                    return std::pair<Node*, Node*>(nullptr, nullptr);
+                }
+            },
+
+            [&](const ast::IndexAccessorExpression* i) {
+                auto [cf1, l1] = ProcessLValueExpression(cf, i->object);
+                auto [cf2, v2] = ProcessExpression(cf1, i->index);
+                l1->AddEdge(v2);
+                return std::pair<Node*, Node*>(cf2, l1);
+            },
+
+            [&](const ast::MemberAccessorExpression* m) {
+                return ProcessLValueExpression(cf, m->structure);
+            },
+
+            [&](const ast::UnaryOpExpression* u) {
+                if (u->op == ast::UnaryOp::kIndirection) {
+                    // Cut the analysis short, since we only need to know the originating variable
+                    // that is being written to.
+                    auto* source_var = sem_.Get(u)->SourceVariable();
+                    auto name = builder_->Symbols().NameFor(source_var->Declaration()->symbol);
+                    auto* deref = CreateNode(name + "_deref");
+                    auto* old_value = current_function_->variables.Set(source_var, deref);
+
+                    // Aggregate values link back to their previous value, as they can never become
+                    // uniform again.
+                    if (!source_var->Type()->UnwrapRef()->UnwrapPtr()->is_scalar() && old_value) {
+                        deref->AddEdge(old_value);
+                    }
+
+                    return std::pair<Node*, Node*>(cf, deref);
+                }
+                return ProcessLValueExpression(cf, u->expr);
+            },
+
+            [&](Default) {
+                TINT_ICE(Resolver, diagnostics_)
+                    << "unknown lvalue expression type: " << std::string(expr->TypeInfo().name);
+                return std::pair<Node*, Node*>(nullptr, nullptr);
+            });
+    }
+
+    /// Process a function call expression.
+    /// @param cf the input control flow node
+    /// @param call the function call to process
+    /// @returns a pair of (control flow node, value node)
+    std::pair<Node*, Node*> ProcessCall(Node* cf, const ast::CallExpression* call) {
+        std::string name;
+        if (call->target.name) {
+            name = builder_->Symbols().NameFor(call->target.name->symbol);
+        } else {
+            name = call->target.type->FriendlyName(builder_->Symbols());
+        }
+
+        // Process call arguments
+        Node* cf_last_arg = cf;
+        std::vector<Node*> args;
+        for (size_t i = 0; i < call->args.size(); i++) {
+            auto [cf_i, arg_i] = ProcessExpression(cf_last_arg, call->args[i]);
+
+            // Capture the index of this argument in a new node.
+            // Note: This is an additional node that isn't described in the specification, for the
+            // purpose of providing diagnostic information.
+            Node* arg_node = CreateNode(name + "_arg_" + std::to_string(i), call);
+            arg_node->type = Node::kFunctionCallArgument;
+            arg_node->arg_index = static_cast<uint32_t>(i);
+            arg_node->AddEdge(arg_i);
+
+            cf_last_arg = cf_i;
+            args.push_back(arg_node);
+        }
+
+        // Note: This is an additional node that isn't described in the specification, for the
+        // purpose of providing diagnostic information.
+        Node* call_node = CreateNode(name + "_call", call);
+        call_node->AddEdge(cf_last_arg);
+
+        Node* result = CreateNode(name + "_return_value", call);
+        result->type = Node::kFunctionCallReturnValue;
+        Node* cf_after = CreateNode("CF_after_" + name, call);
+
+        // Get tags for the callee.
+        CallSiteTag callsite_tag = CallSiteNoRestriction;
+        FunctionTag function_tag = NoRestriction;
+        auto* sem = SemCall(call);
+        const FunctionInfo* func_info = nullptr;
+        Switch(
+            sem->Target(),
+            [&](const sem::Builtin* builtin) {
+                // Most builtins have no restrictions. The exceptions are barriers, derivatives, and
+                // some texture sampling builtins.
+                if (builtin->IsBarrier()) {
+                    callsite_tag = CallSiteRequiredToBeUniform;
+                } else if (builtin->IsDerivative() ||
+                           builtin->Type() == sem::BuiltinType::kTextureSample ||
+                           builtin->Type() == sem::BuiltinType::kTextureSampleBias ||
+                           builtin->Type() == sem::BuiltinType::kTextureSampleCompare) {
+                    callsite_tag = CallSiteRequiredToBeUniform;
+                    function_tag = ReturnValueMayBeNonUniform;
+                } else {
+                    callsite_tag = CallSiteNoRestriction;
+                    function_tag = NoRestriction;
+                }
+            },
+            [&](const sem::Function* func) {
+                // We must have already analyzed the user-defined function since we process
+                // functions in dependency order.
+                TINT_ASSERT(Resolver, functions_.count(func->Declaration()));
+                auto& info = functions_.at(func->Declaration());
+                callsite_tag = info.callsite_tag;
+                function_tag = info.function_tag;
+                func_info = &info;
+            },
+            [&](const sem::TypeConstructor*) {
+                callsite_tag = CallSiteNoRestriction;
+                function_tag = NoRestriction;
+            },
+            [&](const sem::TypeConversion*) {
+                callsite_tag = CallSiteNoRestriction;
+                function_tag = NoRestriction;
+            },
+            [&](Default) {
+                TINT_ICE(Resolver, diagnostics_) << "unhandled function call target: " << name;
+            });
+
+        if (callsite_tag == CallSiteRequiredToBeUniform) {
+            current_function_->required_to_be_uniform->AddEdge(call_node);
+        }
+        cf_after->AddEdge(call_node);
+
+        if (function_tag == SubsequentControlFlowMayBeNonUniform) {
+            cf_after->AddEdge(current_function_->may_be_non_uniform);
+            cf_after->affects_control_flow = true;
+        } else if (function_tag == ReturnValueMayBeNonUniform) {
+            result->AddEdge(current_function_->may_be_non_uniform);
+        }
+
+        result->AddEdge(cf_after);
+
+        // For each argument, add edges based on parameter tags.
+        for (size_t i = 0; i < args.size(); i++) {
+            if (func_info) {
+                switch (func_info->parameters[i].tag) {
+                    case ParameterRequiredToBeUniform:
+                        current_function_->required_to_be_uniform->AddEdge(args[i]);
+                        break;
+                    case ParameterRequiredToBeUniformForSubsequentControlFlow:
+                        cf_after->AddEdge(args[i]);
+                        args[i]->affects_control_flow = true;
+                        break;
+                    case ParameterRequiredToBeUniformForReturnValue:
+                        result->AddEdge(args[i]);
+                        break;
+                    case ParameterNoRestriction:
+                        break;
+                }
+
+                auto* sem_arg = sem_.Get(call->args[i]);
+                if (sem_arg->Type()->Is<sem::Pointer>()) {
+                    auto* ptr_result =
+                        CreateNode(name + "_ptrarg_" + std::to_string(i) + "_result", call);
+                    ptr_result->type = Node::kFunctionCallPointerArgumentResult;
+                    ptr_result->arg_index = static_cast<uint32_t>(i);
+                    if (func_info->parameters[i].pointer_may_become_non_uniform) {
+                        ptr_result->AddEdge(current_function_->may_be_non_uniform);
+                    } else {
+                        // Add edges from the resulting pointer value to any other arguments that
+                        // feed it.
+                        for (auto* source : func_info->parameters[i].pointer_param_output_sources) {
+                            ptr_result->AddEdge(args[source->Index()]);
+                        }
+                    }
+
+                    // Update the current stored value for this pointer argument.
+                    auto* source_var = sem_arg->SourceVariable();
+                    TINT_ASSERT(Resolver, source_var);
+                    current_function_->variables.Set(source_var, ptr_result);
+                }
+            } else {
+                // All builtin function parameters are RequiredToBeUniformForReturnValue, as are
+                // parameters for type constructors and type conversions.
+                // The arrayLength() builtin is a special case, as there is currently no way for it
+                // to have a non-uniform return value.
+                auto* builtin = sem->Target()->As<sem::Builtin>();
+                if (!builtin || builtin->Type() != sem::BuiltinType::kArrayLength) {
+                    result->AddEdge(args[i]);
+                }
+            }
+        }
+
+        return {cf_after, result};
+    }
+
+    /// Traverse a graph starting at `source`, inserting all visited nodes into `reachable` and
+    /// recording which node they were reached from.
+    /// @param source the starting node
+    /// @param reachable the set of reachable nodes to populate, if required
+    void Traverse(Node* source, utils::UniqueVector<Node*>* reachable = nullptr) {
+        std::vector<Node*> to_visit{source};
+
+        while (!to_visit.empty()) {
+            auto* node = to_visit.back();
+            to_visit.pop_back();
+
+            if (reachable) {
+                reachable->add(node);
+            }
+            for (auto* to : node->edges) {
+                if (to->visited_from == nullptr) {
+                    to->visited_from = node;
+                    to_visit.push_back(to);
+                }
+            }
+        }
+    }
+
+    /// Trace back along a path from `start` until finding a node that matches a predicate.
+    /// @param start the starting node
+    /// @param pred the predicate function
+    /// @returns the first node found that matches the predicate, or nullptr
+    template <typename F>
+    Node* TraceBackAlongPathUntil(Node* start, F&& pred) {
+        auto* current = start;
+        while (current) {
+            if (pred(current)) {
+                break;
+            }
+            current = current->visited_from;
+        }
+        return current;
+    }
+
+    /// Recursively descend through the function called by `call` and the functions that it calls in
+    /// order to find a call to a builtin function that requires uniformity.
+    const ast::CallExpression* FindBuiltinThatRequiresUniformity(const ast::CallExpression* call) {
+        auto* target = SemCall(call)->Target();
+        if (target->Is<sem::Builtin>()) {
+            // This is a call to a builtin, so we must be done.
+            return call;
+        } else if (auto* user = target->As<sem::Function>()) {
+            // This is a call to a user-defined function, so inspect the functions called by that
+            // function and look for one whose node has an edge from the RequiredToBeUniform node.
+            auto& target_info = functions_.at(user->Declaration());
+            for (auto* call_node : target_info.required_to_be_uniform->edges) {
+                if (call_node->type == Node::kRegular) {
+                    auto* child_call = call_node->ast->As<ast::CallExpression>();
+                    return FindBuiltinThatRequiresUniformity(child_call);
+                }
+            }
+            TINT_ASSERT(Resolver, false && "unable to find child call with uniformity requirement");
+        } else {
+            TINT_ASSERT(Resolver, false && "unexpected call expression type");
+        }
+        return nullptr;
+    }
+
+    /// Add diagnostic notes to show where control flow became non-uniform on the way to a node.
+    /// @param function the function being analyzed
+    /// @param required_to_be_uniform the node to traverse from
+    /// @param may_be_non_uniform the node to traverse to
+    void ShowCauseOfNonUniformity(FunctionInfo& function,
+                                  Node* required_to_be_uniform,
+                                  Node* may_be_non_uniform) {
+        // Traverse the graph to generate a path from the node to the source of non-uniformity.
+        function.ResetVisited();
+        Traverse(required_to_be_uniform);
+
+        // Get the source of the non-uniform value.
+        auto* non_uniform_source = may_be_non_uniform->visited_from;
+        TINT_ASSERT(Resolver, non_uniform_source);
+
+        // Show where the non-uniform value results in non-uniform control flow.
+        auto* control_flow = TraceBackAlongPathUntil(
+            non_uniform_source, [](Node* node) { return node->affects_control_flow; });
+        if (control_flow) {
+            if (auto* call = control_flow->ast->As<ast::CallExpression>()) {
+                if (control_flow->type == Node::kFunctionCallArgument) {
+                    auto idx = control_flow->arg_index;
+                    diagnostics_.add_note(diag::System::Resolver,
+                                          "non-uniform function call argument causes subsequent "
+                                          "control flow to be non-uniform",
+                                          call->args[idx]->source);
+
+                    // Recurse into the target function.
+                    if (auto* user = SemCall(call)->Target()->As<sem::Function>()) {
+                        auto& callee = functions_.at(user->Declaration());
+                        ShowCauseOfNonUniformity(callee, callee.cf_return,
+                                                 callee.parameters[idx].init_value);
+                    }
+                }
+            } else {
+                diagnostics_.add_note(diag::System::Resolver,
+                                      "control flow depends on non-uniform value",
+                                      control_flow->ast->source);
+            }
+            // TODO(jrprice): There are cases where the function with uniformity requirements is not
+            // actually inside this control flow construct, for example:
+            // - A conditional interrupt (e.g. break), with a barrier elsewhere in the loop
+            // - A conditional assignment to a variable, which is later used to guard a barrier
+            // In these cases, the diagnostics are not entirely accurate as they may not highlight
+            // the actual cause of divergence.
+        }
+
+        // Show the source of the non-uniform value.
+        Switch(
+            non_uniform_source->ast,
+            [&](const ast::IdentifierExpression* ident) {
+                std::string var_type = "";
+                auto* var = sem_.Get<sem::VariableUser>(ident)->Variable();
+                switch (var->StorageClass()) {
+                    case ast::StorageClass::kStorage:
+                        var_type = "read_write storage buffer ";
+                        break;
+                    case ast::StorageClass::kWorkgroup:
+                        var_type = "workgroup storage variable ";
+                        break;
+                    case ast::StorageClass::kPrivate:
+                        var_type = "module-scope private variable ";
+                        break;
+                    default:
+                        if (ast::HasAttribute<ast::BuiltinAttribute>(
+                                var->Declaration()->attributes)) {
+                            var_type = "builtin ";
+                        } else if (ast::HasAttribute<ast::LocationAttribute>(
+                                       var->Declaration()->attributes)) {
+                            var_type = "user-defined input ";
+                        } else {
+                            // TODO(jrprice): Provide more info for this case.
+                        }
+                        break;
+                }
+                diagnostics_.add_note(diag::System::Resolver,
+                                      "reading from " + var_type + "'" +
+                                          builder_->Symbols().NameFor(ident->symbol) +
+                                          "' may result in a non-uniform value",
+                                      ident->source);
+            },
+            [&](const ast::CallExpression* c) {
+                auto target_name = builder_->Symbols().NameFor(
+                    c->target.name->As<ast::IdentifierExpression>()->symbol);
+                switch (non_uniform_source->type) {
+                    case Node::kRegular: {
+                        diagnostics_.add_note(
+                            diag::System::Resolver,
+                            "calling '" + target_name +
+                                "' may cause subsequent control flow to be non-uniform",
+                            c->source);
+
+                        // Recurse into the target function.
+                        if (auto* user = SemCall(c)->Target()->As<sem::Function>()) {
+                            auto& callee = functions_.at(user->Declaration());
+                            ShowCauseOfNonUniformity(callee, callee.cf_return,
+                                                     callee.may_be_non_uniform);
+                        }
+                        break;
+                    }
+                    case Node::kFunctionCallReturnValue: {
+                        diagnostics_.add_note(
+                            diag::System::Resolver,
+                            "return value of '" + target_name + "' may be non-uniform", c->source);
+                        break;
+                    }
+                    case Node::kFunctionCallPointerArgumentResult: {
+                        diagnostics_.add_note(
+                            diag::System::Resolver,
+                            "pointer contents may become non-uniform after calling '" +
+                                target_name + "'",
+                            c->args[non_uniform_source->arg_index]->source);
+                        break;
+                    }
+                    default: {
+                        TINT_ICE(Resolver, diagnostics_) << "unhandled source of non-uniformity";
+                        break;
+                    }
+                }
+            },
+            [&](Default) {
+                TINT_ICE(Resolver, diagnostics_) << "unhandled source of non-uniformity";
+            });
+    }
+
+    /// Generate an error message for a uniformity issue.
+    /// @param function the function that the diagnostic is being produced for
+    /// @param source_node the node that has caused a uniformity issue in `function`
+    /// @param note `true` if the diagnostic should be emitted as a note
+    void MakeError(FunctionInfo& function, Node* source_node, bool note = false) {
+        // Helper to produce a diagnostic message with the severity required by this invocation of
+        // the `MakeError` function.
+        auto report = [&](Source source, std::string msg) {
+            // TODO(jrprice): Switch to error instead of warning when feedback has settled.
+            diag::Diagnostic error{};
+            error.severity = note ? diag::Severity::Note : diag::Severity::Warning;
+            error.system = diag::System::Resolver;
+            error.source = source;
+            error.message = msg;
+            diagnostics_.add(std::move(error));
+        };
+
+        // Traverse the graph to generate a path from RequiredToBeUniform to the source node.
+        function.ResetVisited();
+        Traverse(function.required_to_be_uniform);
+        TINT_ASSERT(Resolver, source_node->visited_from);
+
+        // Find a node that is required to be uniform that has a path to the source node.
+        auto* cause = TraceBackAlongPathUntil(source_node, [&](Node* node) {
+            return node->visited_from == function.required_to_be_uniform;
+        });
+
+        // The node will always have a corresponding call expression.
+        auto* call = cause->ast->As<ast::CallExpression>();
+        TINT_ASSERT(Resolver, call);
+        auto* target = SemCall(call)->Target();
+
+        std::string func_name;
+        if (auto* builtin = target->As<sem::Builtin>()) {
+            func_name = builtin->str();
+        } else if (auto* user = target->As<sem::Function>()) {
+            func_name = builder_->Symbols().NameFor(user->Declaration()->symbol);
+        }
+
+        if (cause->type == Node::kFunctionCallArgument) {
+            // The requirement was on a function parameter.
+            auto param_name = builder_->Symbols().NameFor(
+                target->Parameters()[cause->arg_index]->Declaration()->symbol);
+            report(call->args[cause->arg_index]->source,
+                   "parameter '" + param_name + "' of '" + func_name + "' must be uniform");
+
+            // If this is a call to a user-defined function, add a note to show the reason that the
+            // parameter is required to be uniform.
+            if (auto* user = target->As<sem::Function>()) {
+                auto& next_function = functions_.at(user->Declaration());
+                Node* next_cause = next_function.parameters[cause->arg_index].init_value;
+                MakeError(next_function, next_cause, true);
+            }
+        } else {
+            // The requirement was on a function callsite.
+            report(call->source,
+                   "'" + func_name + "' must only be called from uniform control flow");
+
+            // If this is a call to a user-defined function, add a note to show the builtin that
+            // causes the uniformity requirement.
+            auto* innermost_call = FindBuiltinThatRequiresUniformity(call);
+            if (innermost_call != call) {
+                auto* sem_call = SemCall(call);
+                auto* sem_innermost_call = SemCall(innermost_call);
+
+                // Determine whether the builtin is being called directly or indirectly.
+                bool indirect = false;
+                if (sem_call->Target()->As<sem::Function>() !=
+                    sem_innermost_call->Stmt()->Function()) {
+                    indirect = true;
+                }
+
+                auto* builtin = sem_innermost_call->Target()->As<sem::Builtin>();
+                diagnostics_.add_note(diag::System::Resolver,
+                                      "'" + func_name + "' requires uniformity because it " +
+                                          (indirect ? "indirectly " : "") + "calls " +
+                                          builtin->str(),
+                                      innermost_call->source);
+            }
+        }
+
+        // Show the cause of non-uniformity (starting at the top-level error).
+        if (!note) {
+            ShowCauseOfNonUniformity(function, function.required_to_be_uniform,
+                                     function.may_be_non_uniform);
+        }
+    }
+
+    // Helper for obtaining the sem::Call node for the ast::CallExpression
+    const sem::Call* SemCall(const ast::CallExpression* expr) const {
+        return sem_.Get(expr)->UnwrapMaterialize()->As<sem::Call>();
+    }
+};
+
+}  // namespace
+
+bool AnalyzeUniformity(ProgramBuilder* builder, const DependencyGraph& dependency_graph) {
+    UniformityGraph graph(builder);
+    return graph.Build(dependency_graph);
+}
+
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/uniformity.h b/src/tint/resolver/uniformity.h
new file mode 100644
index 0000000..39827cf
--- /dev/null
+++ b/src/tint/resolver/uniformity.h
@@ -0,0 +1,36 @@
+// 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_RESOLVER_UNIFORMITY_H_
+#define SRC_TINT_RESOLVER_UNIFORMITY_H_
+
+// Forward declarations.
+namespace tint {
+namespace resolver {
+struct DependencyGraph;
+}  // namespace resolver
+class ProgramBuilder;
+}  // namespace tint
+
+namespace tint::resolver {
+
+/// Analyze the uniformity of a program.
+/// @param builder the program to analyze
+/// @param dependency_graph the dependency-ordered module-scope declarations
+/// @returns true if there are no uniformity issues, false otherwise
+bool AnalyzeUniformity(ProgramBuilder* builder, const resolver::DependencyGraph& dependency_graph);
+
+}  // namespace tint::resolver
+
+#endif  // SRC_TINT_RESOLVER_UNIFORMITY_H_
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
new file mode 100644
index 0000000..4a71045
--- /dev/null
+++ b/src/tint/resolver/uniformity_test.cc
@@ -0,0 +1,6256 @@
+// 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 <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/reader/wgsl/parser.h"
+#include "src/tint/resolver/uniformity.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+class UniformityAnalysisTestBase {
+  protected:
+    /// Parse and resolve a WGSL shader.
+    /// @param src the WGSL source code
+    /// @param should_pass true if `src` should pass the analysis, otherwise false
+    void RunTest(std::string src, bool should_pass) {
+        auto file = std::make_unique<Source::File>("test", src);
+        auto program = reader::wgsl::Parse(file.get());
+
+        diag::Formatter::Style style;
+        style.print_newline_at_end = false;
+        error_ = diag::Formatter(style).format(program.Diagnostics());
+
+        bool valid = program.IsValid();
+        if (should_pass) {
+            EXPECT_TRUE(valid) << error_;
+            if (program.Diagnostics().count() == 1u) {
+                EXPECT_THAT(program.Diagnostics().str(), ::testing::HasSubstr("unreachable"));
+            } else {
+                EXPECT_EQ(program.Diagnostics().count(), 0u) << error_;
+            }
+        } else {
+            // TODO(jrprice): expect false when uniformity issues become errors.
+            EXPECT_TRUE(valid) << error_;
+        }
+    }
+
+    /// Build and resolve a program from a ProgramBuilder object.
+    /// @param builder the program builder
+    /// @returns true on success, false on failure
+    bool RunTest(ProgramBuilder&& builder) {
+        auto program = Program(std::move(builder));
+
+        diag::Formatter::Style style;
+        style.print_newline_at_end = false;
+        error_ = diag::Formatter(style).format(program.Diagnostics());
+
+        return program.IsValid();
+    }
+
+    /// The error message from the parser or resolver, if any.
+    std::string error_;
+};
+
+class UniformityAnalysisTest : public UniformityAnalysisTestBase, public ::testing::Test {};
+
+class BasicTest : public UniformityAnalysisTestBase,
+                  public ::testing::TestWithParam<std::tuple<int, int>> {
+  public:
+    /// Enum for the if-statement condition guarding a function call.
+    enum Condition {
+        // Uniform conditions:
+        kTrue,
+        kFalse,
+        kLiteral,
+        kModuleLet,
+        kPipelineOverridable,
+        kFuncLetUniformRhs,
+        kFuncVarUniform,
+        kFuncUniformRetVal,
+        kUniformBuffer,
+        kROStorageBuffer,
+        kLastUniformCondition = kROStorageBuffer,
+        // MayBeNonUniform conditions:
+        kFuncLetNonUniformRhs,
+        kFuncVarNonUniform,
+        kFuncNonUniformRetVal,
+        kRWStorageBuffer,
+        // End of range marker:
+        kEndOfConditionRange,
+    };
+
+    /// Enum for the function call statement.
+    enum Function {
+        // NoRestrictionFunctions:
+        kUserNoRestriction,
+        kMin,
+        kTextureSampleLevel,
+        kLastNoRestrictionFunction = kTextureSampleLevel,
+        // RequiredToBeUniform functions:
+        kUserRequiredToBeUniform,
+        kWorkgroupBarrier,
+        kStorageBarrier,
+        kTextureSample,
+        kTextureSampleBias,
+        kTextureSampleCompare,
+        kDpdx,
+        kDpdxCoarse,
+        kDpdxFine,
+        kDpdy,
+        kDpdyCoarse,
+        kDpdyFine,
+        kFwidth,
+        kFwidthCoarse,
+        kFwidthFine,
+        // End of range marker:
+        kEndOfFunctionRange,
+    };
+
+    /// Convert a condition to its string representation.
+    static std::string ConditionToStr(Condition c) {
+        switch (c) {
+            case kTrue:
+                return "true";
+            case kFalse:
+                return "false";
+            case kLiteral:
+                return "7 == 7";
+            case kModuleLet:
+                return "module_let == 0";
+            case kPipelineOverridable:
+                return "pipeline_overridable == 0";
+            case kFuncLetUniformRhs:
+                return "let_uniform_rhs == 0";
+            case kFuncVarUniform:
+                return "func_uniform == 0";
+            case kFuncUniformRetVal:
+                return "func_uniform_retval() == 0";
+            case kUniformBuffer:
+                return "u == 0";
+            case kROStorageBuffer:
+                return "ro == 0";
+            case kFuncLetNonUniformRhs:
+                return "let_nonuniform_rhs == 0";
+            case kFuncVarNonUniform:
+                return "func_non_uniform == 0";
+            case kFuncNonUniformRetVal:
+                return "func_nonuniform_retval() == 0";
+            case kRWStorageBuffer:
+                return "rw == 0";
+            case kEndOfConditionRange:
+                return "<invalid>";
+        }
+        return "<invalid>";
+    }
+
+    /// Convert a function call to its string representation.
+    static std::string FunctionToStr(Function f) {
+        switch (f) {
+            case kUserNoRestriction:
+                return "user_no_restriction()";
+            case kMin:
+                return "min(1, 1)";
+            case kTextureSampleLevel:
+                return "textureSampleLevel(t, s, vec2(0.5, 0.5), 0.0)";
+            case kUserRequiredToBeUniform:
+                return "user_required_to_be_uniform()";
+            case kWorkgroupBarrier:
+                return "workgroupBarrier()";
+            case kStorageBarrier:
+                return "storageBarrier()";
+            case kTextureSample:
+                return "textureSample(t, s, vec2(0.5, 0.5))";
+            case kTextureSampleBias:
+                return "textureSampleBias(t, s, vec2(0.5, 0.5), 2.0)";
+            case kTextureSampleCompare:
+                return "textureSampleCompare(td, sc, vec2(0.5, 0.5), 0.5)";
+            case kDpdx:
+                return "dpdx(1.0)";
+            case kDpdxCoarse:
+                return "dpdxCoarse(1.0)";
+            case kDpdxFine:
+                return "dpdxFine(1.0)";
+            case kDpdy:
+                return "dpdy(1.0)";
+            case kDpdyCoarse:
+                return "dpdyCoarse(1.0)";
+            case kDpdyFine:
+                return "dpdyFine(1.0)";
+            case kFwidth:
+                return "fwidth(1.0)";
+            case kFwidthCoarse:
+                return "fwidthCoarse(1.0)";
+            case kFwidthFine:
+                return "fwidthFine(1.0)";
+            case kEndOfFunctionRange:
+                return "<invalid>";
+        }
+        return "<invalid>";
+    }
+
+    /// @returns true if `c` is a condition that may be non-uniform.
+    static bool MayBeNonUniform(Condition c) { return c > kLastUniformCondition; }
+
+    /// @returns true if `f` is a function call that is required to be uniform.
+    static bool RequiredToBeUniform(Function f) { return f > kLastNoRestrictionFunction; }
+
+    /// Convert a test parameter pair of condition+function to a string that can be used as part of
+    /// a test name.
+    static std::string ParamsToName(::testing::TestParamInfo<ParamType> params) {
+        Condition c = static_cast<Condition>(std::get<0>(params.param));
+        Function f = static_cast<Function>(std::get<1>(params.param));
+        std::string name;
+#define CASE(c)     \
+    case c:         \
+        name += #c; \
+        break
+
+        switch (c) {
+            CASE(kTrue);
+            CASE(kFalse);
+            CASE(kLiteral);
+            CASE(kModuleLet);
+            CASE(kPipelineOverridable);
+            CASE(kFuncLetUniformRhs);
+            CASE(kFuncVarUniform);
+            CASE(kFuncUniformRetVal);
+            CASE(kUniformBuffer);
+            CASE(kROStorageBuffer);
+            CASE(kFuncLetNonUniformRhs);
+            CASE(kFuncVarNonUniform);
+            CASE(kFuncNonUniformRetVal);
+            CASE(kRWStorageBuffer);
+            case kEndOfConditionRange:
+                break;
+        }
+        name += "_";
+        switch (f) {
+            CASE(kUserNoRestriction);
+            CASE(kMin);
+            CASE(kTextureSampleLevel);
+            CASE(kUserRequiredToBeUniform);
+            CASE(kWorkgroupBarrier);
+            CASE(kStorageBarrier);
+            CASE(kTextureSample);
+            CASE(kTextureSampleBias);
+            CASE(kTextureSampleCompare);
+            CASE(kDpdx);
+            CASE(kDpdxCoarse);
+            CASE(kDpdxFine);
+            CASE(kDpdy);
+            CASE(kDpdyCoarse);
+            CASE(kDpdyFine);
+            CASE(kFwidth);
+            CASE(kFwidthCoarse);
+            CASE(kFwidthFine);
+            case kEndOfFunctionRange:
+                break;
+        }
+#undef CASE
+
+        return name;
+    }
+};
+
+// Test the uniformity constraints for a function call inside a conditional statement.
+TEST_P(BasicTest, ConditionalFunctionCall) {
+    auto condition = static_cast<Condition>(std::get<0>(GetParam()));
+    auto function = static_cast<Function>(std::get<1>(GetParam()));
+    std::string src = R"(
+var<private> p : i32;
+var<workgroup> w : i32;
+@group(0) @binding(0) var<uniform> u : i32;
+@group(0) @binding(0) var<storage, read> ro : i32;
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+@group(1) @binding(0) var t : texture_2d<f32>;
+@group(1) @binding(1) var td : texture_depth_2d;
+@group(1) @binding(2) var s : sampler;
+@group(1) @binding(3) var sc : sampler_comparison;
+
+let module_let : i32 = 42;
+@id(42) override pipeline_overridable : i32;
+
+fn user_no_restriction() {}
+fn user_required_to_be_uniform() { workgroupBarrier(); }
+
+fn func_uniform_retval() -> i32 { return u; }
+fn func_nonuniform_retval() -> i32 { return rw; }
+
+fn foo() {
+  let let_uniform_rhs = 7;
+  let let_nonuniform_rhs = rw;
+
+  var func_uniform = 7;
+  var func_non_uniform = 7;
+  func_non_uniform = rw;
+
+  if ()" + ConditionToStr(condition) +
+                      R"() {
+    )" + FunctionToStr(function) +
+                      R"(;
+  }
+}
+)";
+
+    bool should_pass = !(MayBeNonUniform(condition) && RequiredToBeUniform(function));
+    RunTest(src, should_pass);
+    if (!should_pass) {
+        EXPECT_THAT(error_, ::testing::StartsWith("test:31:5 warning: "));
+        EXPECT_THAT(error_, ::testing::HasSubstr("must only be called from uniform control flow"));
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    UniformityAnalysisTest,
+    BasicTest,
+    ::testing::Combine(::testing::Range<int>(0, BasicTest::kEndOfConditionRange),
+                       ::testing::Range<int>(0, BasicTest::kEndOfFunctionRange)),
+    BasicTest::ParamsToName);
+
+////////////////////////////////////////////////////////////////////////////////
+/// Test specific function and parameter tags that are not tested above.
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(UniformityAnalysisTest, SubsequentControlFlowMayBeNonUniform_Pass) {
+    // Call a function that causes subsequent control flow to be non-uniform, and then call another
+    // function that doesn't require uniformity.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+var<private> p : i32;
+
+fn foo() {
+  if (rw == 0) {
+    p = 42;
+    return;
+  }
+  p = 5;
+  return;
+}
+
+fn bar() {
+  if (p == 42) {
+    p = 7;
+  }
+}
+
+fn main() {
+  foo();
+  bar();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, SubsequentControlFlowMayBeNonUniform_Fail) {
+    // Call a function that causes subsequent control flow to be non-uniform, and then call another
+    // function that requires uniformity.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+var<private> p : i32;
+
+fn foo() {
+  if (rw == 0) {
+    p = 42;
+    return;
+  }
+  p = 5;
+  return;
+}
+
+fn main() {
+  foo();
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:17:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:16:3 note: calling 'foo' may cause subsequent control flow to be non-uniform
+  foo();
+  ^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (rw == 0) {
+  ^^
+
+test:7:7 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  if (rw == 0) {
+      ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, SubsequentControlFlowMayBeNonUniform_Nested_Fail) {
+    // Indirectly call a function that causes subsequent control flow to be non-uniform, and then
+    // call another function that requires uniformity.
+    // The lack of return statement in `foo()` requires that we implicitly add an edge from
+    // CF_return to that last control flow node of the function.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+var<private> p : i32;
+
+fn bar() {
+  if (rw == 0) {
+    p = 42;
+    return;
+  }
+  p = 5;
+  return;
+}
+
+fn foo() {
+  bar();
+}
+
+fn main() {
+  foo();
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:21:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:20:3 note: calling 'foo' may cause subsequent control flow to be non-uniform
+  foo();
+  ^^^
+
+test:16:3 note: calling 'bar' may cause subsequent control flow to be non-uniform
+  bar();
+  ^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (rw == 0) {
+  ^^
+
+test:7:7 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  if (rw == 0) {
+      ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ParameterNoRestriction_Pass) {
+    // Pass a non-uniform value as an argument, and then try to use the return value for
+    // control-flow guarding a barrier.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+var<private> p : i32;
+
+fn foo(i : i32) -> i32 {
+  if (i == 0) {
+    // This assignment is non-uniform, but shouldn't affect the return value.
+    p = 42;
+  }
+  return 7;
+}
+
+fn bar() {
+  let x = foo(rw);
+  if (x == 7) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ParameterRequiredToBeUniform_Pass) {
+    // Pass a uniform value as an argument to a function that uses that parameter for control-flow
+    // guarding a barrier.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> ro : i32;
+
+fn foo(i : i32) {
+  if (i == 0) {
+    workgroupBarrier();
+  }
+}
+
+fn bar() {
+  foo(ro);
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ParameterRequiredToBeUniform_Fail) {
+    // Pass a non-uniform value as an argument to a function that uses that parameter for
+    // control-flow guarding a barrier.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo(i : i32) {
+  if (i == 0) {
+    workgroupBarrier();
+  }
+}
+
+fn bar() {
+  foo(rw);
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:11:7 warning: parameter 'i' of 'foo' must be uniform
+  foo(rw);
+      ^^
+
+test:6:5 note: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:11:7 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  foo(rw);
+      ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ParameterRequiredToBeUniformForReturnValue_Pass) {
+    // Pass a uniform value as an argument to a function that uses that parameter to produce the
+    // return value, and then use the return value for control-flow guarding a barrier.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> ro : i32;
+
+fn foo(i : i32) -> i32 {
+  return 1 + i;
+}
+
+fn bar() {
+  if (foo(ro) == 7) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ParameterRequiredToBeUniformForReturnValue_Fail) {
+    // Pass a non-uniform value as an argument to a function that uses that parameter to produce the
+    // return value, and then use the return value for control-flow guarding a barrier.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo(i : i32) -> i32 {
+  return 1 + i;
+}
+
+fn bar() {
+  if (foo(rw) == 7) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:9:3 note: control flow depends on non-uniform value
+  if (foo(rw) == 7) {
+  ^^
+
+test:9:11 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  if (foo(rw) == 7) {
+          ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ParameterRequiredToBeUniformForSubsequentControlFlow_Pass) {
+    // Pass a uniform value as an argument to a function that uses that parameter return early, and
+    // then invoke a barrier after calling that function.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> ro : i32;
+
+var<private> p : i32;
+
+fn foo(i : i32) {
+  if (i == 0) {
+    p = 42;
+    return;
+  }
+  p = 5;
+  return;
+}
+
+fn bar() {
+  foo(ro);
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ParameterRequiredToBeUniformForSubsequentControlFlow_Fail) {
+    // Pass a non-uniform value as an argument to a function that uses that parameter return early,
+    // and then invoke a barrier after calling that function.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+var<private> p : i32;
+
+fn foo(i : i32) {
+  if (i == 0) {
+    p = 42;
+    return;
+  }
+  p = 5;
+  return;
+}
+
+fn bar() {
+  foo(rw);
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:17:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:16:7 note: non-uniform function call argument causes subsequent control flow to be non-uniform
+  foo(rw);
+      ^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (i == 0) {
+  ^^
+
+test:7:7 note: reading from 'i' may result in a non-uniform value
+  if (i == 0) {
+      ^
+
+test:16:7 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  foo(rw);
+      ^^
+)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Test shader IO attributes.
+////////////////////////////////////////////////////////////////////////////////
+
+struct BuiltinEntry {
+    std::string name;
+    std::string type;
+    bool uniform;
+    BuiltinEntry(std::string n, std::string t, bool u) : name(n), type(t), uniform(u) {}
+};
+
+class ComputeBuiltin : public UniformityAnalysisTestBase,
+                       public ::testing::TestWithParam<BuiltinEntry> {};
+TEST_P(ComputeBuiltin, AsParam) {
+    std::string src = R"(
+@stage(compute) @workgroup_size(64)
+fn main(@builtin()" + GetParam().name +
+                      R"() b : )" + GetParam().type + R"() {
+  if (all(vec3(b) == vec3(0u))) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    bool should_pass = GetParam().uniform;
+    RunTest(src, should_pass);
+    if (!should_pass) {
+        EXPECT_EQ(
+            error_,
+            R"(test:5:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:4:3 note: control flow depends on non-uniform value
+  if (all(vec3(b) == vec3(0u))) {
+  ^^
+
+test:4:16 note: reading from builtin 'b' may result in a non-uniform value
+  if (all(vec3(b) == vec3(0u))) {
+               ^
+)");
+    }
+}
+
+TEST_P(ComputeBuiltin, InStruct) {
+    std::string src = R"(
+struct S {
+  @builtin()" + GetParam().name +
+                      R"() b : )" + GetParam().type + R"(
+}
+
+@stage(compute) @workgroup_size(64)
+fn main(s : S) {
+  if (all(vec3(s.b) == vec3(0u))) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    bool should_pass = GetParam().uniform;
+    RunTest(src, should_pass);
+    if (!should_pass) {
+        EXPECT_EQ(
+            error_,
+            R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (all(vec3(s.b) == vec3(0u))) {
+  ^^
+
+test:8:16 note: reading from 's' may result in a non-uniform value
+  if (all(vec3(s.b) == vec3(0u))) {
+               ^
+)");
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
+                         ComputeBuiltin,
+                         ::testing::Values(BuiltinEntry{"local_invocation_id", "vec3<u32>", false},
+                                           BuiltinEntry{"local_invocation_index", "u32", false},
+                                           BuiltinEntry{"global_invocation_id", "vec3<u32>", false},
+                                           BuiltinEntry{"workgroup_id", "vec3<u32>", true},
+                                           BuiltinEntry{"num_workgroups", "vec3<u32>", true}),
+                         [](const ::testing::TestParamInfo<ComputeBuiltin::ParamType>& p) {
+                             return p.param.name;
+                         });
+
+TEST_F(UniformityAnalysisTest, ComputeBuiltin_MixedAttributesInStruct) {
+    // Mix both non-uniform and uniform shader IO attributes in the same structure. Even accessing
+    // just uniform member causes non-uniformity in this case.
+    std::string src = R"(
+struct S {
+  @builtin(num_workgroups) num_groups : vec3<u32>,
+  @builtin(local_invocation_index) idx : u32,
+}
+
+@stage(compute) @workgroup_size(64)
+fn main(s : S) {
+  if (s.num_groups.x == 0u) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:9:3 note: control flow depends on non-uniform value
+  if (s.num_groups.x == 0u) {
+  ^^
+
+test:9:7 note: reading from 's' may result in a non-uniform value
+  if (s.num_groups.x == 0u) {
+      ^
+)");
+}
+
+class FragmentBuiltin : public UniformityAnalysisTestBase,
+                        public ::testing::TestWithParam<BuiltinEntry> {};
+TEST_P(FragmentBuiltin, AsParam) {
+    std::string src = R"(
+@stage(fragment)
+fn main(@builtin()" + GetParam().name +
+                      R"() b : )" + GetParam().type + R"() {
+  if (u32(vec4(b).x) == 0u) {
+    dpdx(0.5);
+  }
+}
+)";
+
+    bool should_pass = GetParam().uniform;
+    RunTest(src, should_pass);
+    if (!should_pass) {
+        EXPECT_EQ(error_,
+                  R"(test:5:5 warning: 'dpdx' must only be called from uniform control flow
+    dpdx(0.5);
+    ^^^^
+
+test:4:3 note: control flow depends on non-uniform value
+  if (u32(vec4(b).x) == 0u) {
+  ^^
+
+test:4:16 note: reading from builtin 'b' may result in a non-uniform value
+  if (u32(vec4(b).x) == 0u) {
+               ^
+)");
+    }
+}
+
+TEST_P(FragmentBuiltin, InStruct) {
+    std::string src = R"(
+struct S {
+  @builtin()" + GetParam().name +
+                      R"() b : )" + GetParam().type + R"(
+}
+
+@stage(fragment)
+fn main(s : S) {
+  if (u32(vec4(s.b).x) == 0u) {
+    dpdx(0.5);
+  }
+}
+)";
+
+    bool should_pass = GetParam().uniform;
+    RunTest(src, should_pass);
+    if (!should_pass) {
+        EXPECT_EQ(error_,
+                  R"(test:9:5 warning: 'dpdx' must only be called from uniform control flow
+    dpdx(0.5);
+    ^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (u32(vec4(s.b).x) == 0u) {
+  ^^
+
+test:8:16 note: reading from 's' may result in a non-uniform value
+  if (u32(vec4(s.b).x) == 0u) {
+               ^
+)");
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
+                         FragmentBuiltin,
+                         ::testing::Values(BuiltinEntry{"position", "vec4<f32>", false},
+                                           BuiltinEntry{"front_facing", "bool", false},
+                                           BuiltinEntry{"sample_index", "u32", false},
+                                           BuiltinEntry{"sample_mask", "u32", false}),
+                         [](const ::testing::TestParamInfo<FragmentBuiltin::ParamType>& p) {
+                             return p.param.name;
+                         });
+
+TEST_F(UniformityAnalysisTest, FragmentLocation) {
+    std::string src = R"(
+@stage(fragment)
+fn main(@location(0) l : f32) {
+  if (l == 0.0) {
+    dpdx(0.5);
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:5:5 warning: 'dpdx' must only be called from uniform control flow
+    dpdx(0.5);
+    ^^^^
+
+test:4:3 note: control flow depends on non-uniform value
+  if (l == 0.0) {
+  ^^
+
+test:4:7 note: reading from user-defined input 'l' may result in a non-uniform value
+  if (l == 0.0) {
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, FragmentLocation_InStruct) {
+    std::string src = R"(
+struct S {
+  @location(0) l : f32
+}
+
+@stage(fragment)
+fn main(s : S) {
+  if (s.l == 0.0) {
+    dpdx(0.5);
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:5 warning: 'dpdx' must only be called from uniform control flow
+    dpdx(0.5);
+    ^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (s.l == 0.0) {
+  ^^
+
+test:8:7 note: reading from 's' may result in a non-uniform value
+  if (s.l == 0.0) {
+      ^
+)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Test loop conditions and conditional break/continue statements.
+////////////////////////////////////////////////////////////////////////////////
+
+namespace LoopTest {
+
+enum ControlFlowInterrupt {
+    kBreak,
+    kContinue,
+    kReturn,
+    kDiscard,
+};
+enum Condition {
+    kNone,
+    kUniform,
+    kNonUniform,
+};
+
+using LoopTestParams = std::tuple<int, int>;
+
+static std::string ToStr(ControlFlowInterrupt interrupt) {
+    switch (interrupt) {
+        case kBreak:
+            return "break";
+        case kContinue:
+            return "continue";
+        case kReturn:
+            return "return";
+        case kDiscard:
+            return "discard";
+    }
+    return "";
+}
+
+static std::string ToStr(Condition condition) {
+    switch (condition) {
+        case kNone:
+            return "uncondtiional";
+        case kUniform:
+            return "uniform";
+        case kNonUniform:
+            return "nonuniform";
+    }
+    return "";
+}
+
+class LoopTest : public UniformityAnalysisTestBase,
+                 public ::testing::TestWithParam<LoopTestParams> {
+  protected:
+    std::string MakeInterrupt(ControlFlowInterrupt interrupt, Condition condition) {
+        switch (condition) {
+            case kNone:
+                return ToStr(interrupt);
+            case kUniform:
+                return "if (uniform_var == 42) { " + ToStr(interrupt) + "; }";
+            case kNonUniform:
+                return "if (nonuniform_var == 42) { " + ToStr(interrupt) + "; }";
+        }
+        return "<invalid>";
+    }
+};
+
+INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
+                         LoopTest,
+                         ::testing::Combine(::testing::Range<int>(0, kDiscard + 1),
+                                            ::testing::Range<int>(0, kNonUniform + 1)),
+                         [](const ::testing::TestParamInfo<LoopTestParams>& p) {
+                             ControlFlowInterrupt interrupt =
+                                 static_cast<ControlFlowInterrupt>(std::get<0>(p.param));
+                             auto condition = static_cast<Condition>(std::get<1>(p.param));
+                             return ToStr(interrupt) + "_" + ToStr(condition);
+                         });
+
+TEST_P(LoopTest, CallInBody_InterruptAfter) {
+    // Test control-flow interrupt in a loop after a function call that requires uniform control
+    // flow.
+    auto interrupt = static_cast<ControlFlowInterrupt>(std::get<0>(GetParam()));
+    auto condition = static_cast<Condition>(std::get<1>(GetParam()));
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> uniform_var : i32;
+@group(0) @binding(0) var<storage, read_write> nonuniform_var : i32;
+
+fn foo() {
+  loop {
+    // Pretend that this isn't an infinite loop, in case the interrupt is a
+    // continue statement.
+    if (false) {
+      break;
+    }
+
+    workgroupBarrier();
+    )" + MakeInterrupt(interrupt, condition) +
+                      R"(;
+  }
+}
+)";
+
+    if (condition == kNonUniform) {
+        RunTest(src, false);
+        EXPECT_THAT(
+            error_,
+            ::testing::StartsWith(
+                R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();)"));
+        EXPECT_THAT(error_,
+                    ::testing::HasSubstr("test:14:9 note: reading from read_write storage buffer "
+                                         "'nonuniform_var' may result in a non-uniform value"));
+    } else {
+        RunTest(src, true);
+    }
+}
+
+TEST_P(LoopTest, CallInBody_InterruptBefore) {
+    // Test control-flow interrupt in a loop before a function call that requires uniform control
+    // flow.
+    auto interrupt = static_cast<ControlFlowInterrupt>(std::get<0>(GetParam()));
+    auto condition = static_cast<Condition>(std::get<1>(GetParam()));
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> uniform_var : i32;
+@group(0) @binding(0) var<storage, read_write> nonuniform_var : i32;
+
+fn foo() {
+  loop {
+    // Pretend that this isn't an infinite loop, in case the interrupt is a
+    // continue statement.
+    if (false) {
+      break;
+    }
+
+    )" + MakeInterrupt(interrupt, condition) +
+                      R"(;
+    workgroupBarrier();
+  }
+}
+)";
+
+    if (condition == kNonUniform) {
+        RunTest(src, false);
+
+        EXPECT_THAT(
+            error_,
+            ::testing::StartsWith(
+                R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();)"));
+        EXPECT_THAT(error_,
+                    ::testing::HasSubstr("test:13:9 note: reading from read_write storage buffer "
+                                         "'nonuniform_var' may result in a non-uniform value"));
+    } else {
+        RunTest(src, true);
+    }
+}
+
+TEST_P(LoopTest, CallInContinuing_InterruptInBody) {
+    // Test control-flow interrupt in a loop with a function call that requires uniform control flow
+    // in the continuing statement.
+    auto interrupt = static_cast<ControlFlowInterrupt>(std::get<0>(GetParam()));
+    auto condition = static_cast<Condition>(std::get<1>(GetParam()));
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> uniform_var : i32;
+@group(0) @binding(0) var<storage, read_write> nonuniform_var : i32;
+
+fn foo() {
+  loop {
+    // Pretend that this isn't an infinite loop, in case the interrupt is a
+    // continue statement.
+    if (false) {
+      break;
+    }
+
+    )" + MakeInterrupt(interrupt, condition) +
+                      R"(;
+    continuing {
+      workgroupBarrier();
+    }
+  }
+}
+)";
+
+    if (condition == kNonUniform) {
+        RunTest(src, false);
+        EXPECT_THAT(
+            error_,
+            ::testing::StartsWith(
+                R"(test:15:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();)"));
+        EXPECT_THAT(error_,
+                    ::testing::HasSubstr("test:13:9 note: reading from read_write storage buffer "
+                                         "'nonuniform_var' may result in a non-uniform value"));
+    } else {
+        RunTest(src, true);
+    }
+}
+
+TEST_F(UniformityAnalysisTest, Loop_CallInBody_UniformBreakInContinuing) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> n : i32;
+
+fn foo() {
+  var i = 0;
+  loop {
+    workgroupBarrier();
+    continuing {
+      i = i + 1;
+      if (i == n) {
+        break;
+      }
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_CallInBody_NonUniformBreakInContinuing) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn foo() {
+  var i = 0;
+  loop {
+    workgroupBarrier();
+    continuing {
+      i = i + 1;
+      if (i == n) {
+        break;
+      }
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:10:7 note: control flow depends on non-uniform value
+      if (i == n) {
+      ^^
+
+test:10:16 note: reading from read_write storage buffer 'n' may result in a non-uniform value
+      if (i == n) {
+               ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Loop_CallInContinuing_UniformBreakInContinuing) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> n : i32;
+
+fn foo() {
+  var i = 0;
+  loop {
+    continuing {
+      workgroupBarrier();
+      i = i + 1;
+      if (i == n) {
+        break;
+      }
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_CallInContinuing_NonUniformBreakInContinuing) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn foo() {
+  var i = 0;
+  loop {
+    continuing {
+      workgroupBarrier();
+      i = i + 1;
+      if (i == n) {
+        break;
+      }
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:10:7 note: control flow depends on non-uniform value
+      if (i == n) {
+      ^^
+
+test:10:16 note: reading from read_write storage buffer 'n' may result in a non-uniform value
+      if (i == n) {
+               ^
+)");
+}
+
+class LoopDeadCodeTest : public UniformityAnalysisTestBase, public ::testing::TestWithParam<int> {};
+
+INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
+                         LoopDeadCodeTest,
+                         ::testing::Range<int>(0, kDiscard + 1),
+                         [](const ::testing::TestParamInfo<LoopDeadCodeTest::ParamType>& p) {
+                             return ToStr(static_cast<ControlFlowInterrupt>(p.param));
+                         });
+
+TEST_P(LoopDeadCodeTest, AfterInterrupt) {
+    // Dead code after a control-flow interrupt in a loop shouldn't cause uniformity errors.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn foo() {
+  loop {
+    )" + ToStr(static_cast<ControlFlowInterrupt>(GetParam())) +
+                      R"(;
+    if (n == 42) {
+      workgroupBarrier();
+    }
+    continuing {
+      // Pretend that this isn't an infinite loop, in case the interrupt is a
+      // continue statement.
+      if (false) {
+        break;
+      }
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_VarBecomesNonUniformInLoopAfterBarrier) {
+    // Use a variable for a conditional barrier in a loop, and then assign a non-uniform value to
+    // that variable later in that loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (v == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    v = non_uniform;
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:7:5 note: control flow depends on non-uniform value
+    if (v == 0) {
+    ^^
+
+test:12:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Loop_VarBecomesNonUniformInLoopAfterBarrier_BreakAtEnd) {
+    // Use a variable for a conditional barrier in a loop, and then assign a non-uniform value to
+    // that variable later in that loop. End the loop with a break statement to prevent the
+    // non-uniform value from causing an issue.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (v == 0) {
+      workgroupBarrier();
+    }
+
+    v = non_uniform;
+    break;
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_ConditionalAssignNonUniformWithBreak_BarrierInLoop) {
+    // In a conditional block, assign a non-uniform value and then break, then use a variable for a
+    // conditional barrier later in the loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (true) {
+      v = non_uniform;
+      break;
+    }
+    if (v == 0) {
+      workgroupBarrier();
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_ConditionalAssignNonUniformWithConditionalBreak_BarrierInLoop) {
+    // In a conditional block, assign a non-uniform value and then conditionally break, then use a
+    // variable for a conditional barrier later in the loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (true) {
+      v = non_uniform;
+      if (true) {
+        break;
+      }
+    }
+    if (v == 0) {
+      workgroupBarrier();
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:13:5 note: control flow depends on non-uniform value
+    if (v == 0) {
+    ^^
+
+test:8:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+      v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Loop_ConditionalAssignNonUniformWithBreak_BarrierAfterLoop) {
+    // In a conditional block, assign a non-uniform value and then break, then use a variable for a
+    // conditional barrier after the loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (true) {
+      v = non_uniform;
+      break;
+    }
+    v = 5;
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:14:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:8:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+      v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Loop_VarBecomesUniformBeforeSomeExits_BarrierAfterLoop) {
+    // Assign a non-uniform value, have two exit points only one of which assigns a uniform value,
+    // then use a variable for a conditional barrier after the loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (true) {
+      break;
+    }
+
+    v = non_uniform;
+
+    if (false) {
+      v = 6;
+      break;
+    }
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:20:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:19:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:11:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Loop_VarBecomesUniformBeforeAllExits_BarrierAfterLoop) {
+    // Assign a non-uniform value, have two exit points both of which assigns a uniform value,
+    // then use a variable for a conditional barrier after the loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (true) {
+      v = 5;
+      break;
+    }
+
+    v = non_uniform;
+
+    if (false) {
+      v = 6;
+      break;
+    }
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_AssignNonUniformBeforeConditionalBreak_BarrierAfterLoop) {
+    // Assign a non-uniform value and then break in a conditional block, then use a variable for a
+    // conditional barrier after the loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    v = non_uniform;
+    if (true) {
+      if (false) {
+        v = 5;
+      } else {
+        break;
+      }
+      v = 5;
+    }
+    v = 5;
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:20:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:19:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:7:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Loop_VarBecomesNonUniformBeforeConditionalContinue_BarrierAtStart) {
+    // Use a variable for a conditional barrier in a loop, assign a non-uniform value to
+    // that variable later in that loop, then perform a conditional continue before assigning a
+    // uniform value to that variable.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (v == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    v = non_uniform;
+    if (true) {
+      continue;
+    }
+
+    v = 5;
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:7:5 note: control flow depends on non-uniform value
+    if (v == 0) {
+    ^^
+
+test:12:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest,
+       Loop_VarBecomesUniformBeforeConditionalContinue_BarrierInContinuing) {
+    // Use a variable for a conditional barrier in the continuing statement of a loop, assign a
+    // non-uniform value to that variable later in that loop, then conditionally assign a uniform
+    // value before continuing.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    v = non_uniform;
+
+    if (false) {
+      v = 5;
+      continue;
+    }
+
+    continuing {
+      if (v == 0) {
+        workgroupBarrier();
+      }
+      if (true) {
+        break;
+      }
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:16:9 warning: 'workgroupBarrier' must only be called from uniform control flow
+        workgroupBarrier();
+        ^^^^^^^^^^^^^^^^
+
+test:15:7 note: control flow depends on non-uniform value
+      if (v == 0) {
+      ^^
+
+test:7:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Loop_VarBecomesNonUniformBeforeConditionalContinue) {
+    // Use a variable for a conditional barrier in a loop, assign a non-uniform value to
+    // that variable later in that loop, then perform a conditional continue before assigning a
+    // uniform value to that variable.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (v == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    v = non_uniform;
+    if (true) {
+      continue;
+    }
+
+    v = 5;
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:7:5 note: control flow depends on non-uniform value
+    if (v == 0) {
+    ^^
+
+test:12:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Loop_VarBecomesNonUniformInNestedLoopWithBreak_BarrierInLoop) {
+    // Use a variable for a conditional barrier in a loop, then conditionally assign a non-uniform
+    // value to that variable followed by a break in a nested loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (v == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    loop {
+      if (true) {
+        v = non_uniform;
+        break;
+      }
+      v = 5;
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:7:5 note: control flow depends on non-uniform value
+    if (v == 0) {
+    ^^
+
+test:14:13 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+        v = non_uniform;
+            ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest,
+       Loop_VarBecomesNonUniformInNestedLoopWithBreak_BecomesUniformAgain_BarrierAfterLoop) {
+    // Conditionally assign a non-uniform value followed by a break in a nested loop, assign a
+    // uniform value in the outer loop, and then use a variable for a conditional barrier after the
+    // loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  loop {
+    if (false) {
+      break;
+    }
+
+    loop {
+      if (true) {
+        v = non_uniform;
+        break;
+      }
+    }
+    v = 5;
+  }
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_NonUniformValueNeverReachesContinuing) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  loop {
+    var v = non_uniform;
+    return;
+
+    continuing {
+      if (v == 0) {
+        workgroupBarrier();
+      }
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_NonUniformBreakInBody_Reconverge) {
+    // Loops reconverge at exit, so test that we can call workgroupBarrier() after a loop that
+    // contains a non-uniform conditional break.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn foo() {
+  var i = 0;
+  loop {
+    if (i == n) {
+      break;
+    }
+    i = i + 1;
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_NonUniformFunctionInBody_Reconverge) {
+    // Loops reconverge at exit, so test that we can call workgroupBarrier() after a loop that
+    // contains a call to a function that causes non-uniform control flow.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn bar() {
+  if (n == 42) {
+    return;
+  } else {
+    return;
+  }
+}
+
+fn foo() {
+  loop {
+    bar();
+    break;
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Loop_NonUniformFunctionDiscard_NoReconvergence) {
+    // Loops should not reconverge after non-uniform discard statements.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn bar() {
+  if (n == 42) {
+    discard;
+  }
+}
+
+fn foo() {
+  loop {
+    bar();
+    break;
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:15:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:12:5 note: calling 'bar' may cause subsequent control flow to be non-uniform
+    bar();
+    ^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (n == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'n' may result in a non-uniform value
+  if (n == 42) {
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_CallInside_UniformCondition) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> n : i32;
+
+fn foo() {
+  for (var i = 0; i < n; i = i + 1) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_CallInside_NonUniformCondition) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn foo() {
+  for (var i = 0; i < n; i = i + 1) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  for (var i = 0; i < n; i = i + 1) {
+  ^^^
+
+test:5:23 note: reading from read_write storage buffer 'n' may result in a non-uniform value
+  for (var i = 0; i < n; i = i + 1) {
+                      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_CallInside_InitializerCausesNonUniformFlow) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn bar() -> i32 {
+  if (n == 42) {
+    return 1;
+  } else {
+    return 2;
+  }
+}
+
+fn foo() {
+  for (var i = bar(); i < 10; i = i + 1) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:13:16 note: calling 'bar' may cause subsequent control flow to be non-uniform
+  for (var i = bar(); i < 10; i = i + 1) {
+               ^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (n == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'n' may result in a non-uniform value
+  if (n == 42) {
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_CallInside_ContinuingCausesNonUniformFlow) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn bar() -> i32 {
+  if (n == 42) {
+    return 1;
+  } else {
+    return 2;
+  }
+}
+
+fn foo() {
+  for (var i = 0; i < 10; i = i + bar()) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:13:35 note: calling 'bar' may cause subsequent control flow to be non-uniform
+  for (var i = 0; i < 10; i = i + bar()) {
+                                  ^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (n == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'n' may result in a non-uniform value
+  if (n == 42) {
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_VarBecomesNonUniformInContinuing_BarrierInLoop) {
+    // Use a variable for a conditional barrier in a loop, and then assign a non-uniform value to
+    // that variable in the continuing statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; v = non_uniform) {
+    if (v == 0) {
+      workgroupBarrier();
+      break;
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:7:5 note: control flow depends on non-uniform value
+    if (v == 0) {
+    ^^
+
+test:6:31 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  for (var i = 0; i < 10; v = non_uniform) {
+                              ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_VarBecomesUniformInContinuing_BarrierInLoop) {
+    // Use a variable for a conditional barrier in a loop, and then assign a uniform value to that
+    // variable in the continuing statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; v = 5) {
+    if (v == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    v = non_uniform;
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_VarBecomesNonUniformInContinuing_BarrierAfterLoop) {
+    // Use a variable for a conditional barrier after a loop, and assign a non-uniform value to
+    // that variable in the continuing statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; v = non_uniform) {
+    v = 5;
+  }
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:9:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:6:31 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  for (var i = 0; i < 10; v = non_uniform) {
+                              ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_VarBecomesUniformInContinuing_BarrierAfterLoop) {
+    // Use a variable for a conditional barrier after a loop, and assign a uniform value to that
+    // variable in the continuing statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; v = 5) {
+    v = non_uniform;
+  }
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_VarBecomesNonUniformInLoopAfterBarrier) {
+    // Use a variable for a conditional barrier in a loop, and then assign a non-uniform value to
+    // that variable later in that loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; i++) {
+    if (v == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    v = non_uniform;
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:7:5 note: control flow depends on non-uniform value
+    if (v == 0) {
+    ^^
+
+test:12:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_ConditionalAssignNonUniformWithBreak_BarrierInLoop) {
+    // In a conditional block, assign a non-uniform value and then break, then use a variable for a
+    // conditional barrier later in the loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; i++) {
+    if (true) {
+      v = non_uniform;
+      break;
+    }
+    if (v == 0) {
+      workgroupBarrier();
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_ConditionalAssignNonUniformWithBreak_BarrierAfterLoop) {
+    // In a conditional block, assign a non-uniform value and then break, then use a variable for a
+    // conditional barrier after the loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; i++) {
+    if (true) {
+      v = non_uniform;
+      break;
+    }
+    v = 5;
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:14:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:8:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+      v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_VarRemainsNonUniformAtLoopEnd_BarrierAfterLoop) {
+    // Assign a non-uniform value, assign a uniform value before all explicit break points but leave
+    // the value non-uniform at loop exit, then use a variable for a conditional barrier after the
+    // loop.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; i++) {
+    if (true) {
+      v = 5;
+      break;
+    }
+
+    v = non_uniform;
+
+    if (true) {
+      v = 6;
+      break;
+    }
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:20:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:12:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest,
+       ForLoop_VarBecomesNonUniformBeforeConditionalContinue_BarrierAtStart) {
+    // Use a variable for a conditional barrier in a loop, assign a non-uniform value to
+    // that variable later in that loop, then perform a conditional continue before assigning a
+    // uniform value to that variable.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; i++) {
+    if (v == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    v = non_uniform;
+    if (true) {
+      continue;
+    }
+
+    v = 5;
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:7:5 note: control flow depends on non-uniform value
+    if (v == 0) {
+    ^^
+
+test:12:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_VarBecomesNonUniformBeforeConditionalContinue) {
+    // Use a variable for a conditional barrier in a loop, assign a non-uniform value to
+    // that variable later in that loop, then perform a conditional continue before assigning a
+    // uniform value to that variable.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  for (var i = 0; i < 10; i++) {
+    if (v == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    v = non_uniform;
+    if (true) {
+      continue;
+    }
+
+    v = 5;
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:7:5 note: control flow depends on non-uniform value
+    if (v == 0) {
+    ^^
+
+test:12:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+    v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ForLoop_NonUniformCondition_Reconverge) {
+    // Loops reconverge at exit, so test that we can call workgroupBarrier() after a loop that has a
+    // non-uniform condition.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn foo() {
+  for (var i = 0; i < n; i = i + 1) {
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+}  // namespace LoopTest
+
+////////////////////////////////////////////////////////////////////////////////
+/// If-else statement tests.
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(UniformityAnalysisTest, IfElse_UniformCondition_BarrierInTrueBlock) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> uniform_global : i32;
+
+fn foo() {
+  if (uniform_global == 42) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_UniformCondition_BarrierInElseBlock) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> uniform_global : i32;
+
+fn foo() {
+  if (uniform_global == 42) {
+  } else {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_UniformCondition_BarrierInElseIfBlock) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> uniform_global : i32;
+
+fn foo() {
+  if (uniform_global == 42) {
+  } else if (true) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_NonUniformCondition_BarrierInTrueBlock) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  if (non_uniform == 42) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (non_uniform == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  if (non_uniform == 42) {
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_NonUniformCondition_BarrierInElseBlock) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  if (non_uniform == 42) {
+  } else {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (non_uniform == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  if (non_uniform == 42) {
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_ShortCircuitingCondition_NonUniformLHS_And) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+var<private> p : i32;
+
+fn main() {
+  if ((non_uniform_global == 42) && false) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:34 note: control flow depends on non-uniform value
+  if ((non_uniform_global == 42) && false) {
+                                 ^^
+
+test:7:8 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  if ((non_uniform_global == 42) && false) {
+       ^^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_ShortCircuitingCondition_NonUniformRHS_And) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+var<private> p : i32;
+
+fn main() {
+  if (false && (non_uniform_global == 42)) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (false && (non_uniform_global == 42)) {
+  ^^
+
+test:7:17 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  if (false && (non_uniform_global == 42)) {
+                ^^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_ShortCircuitingCondition_NonUniformLHS_Or) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+var<private> p : i32;
+
+fn main() {
+  if ((non_uniform_global == 42) || true) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:34 note: control flow depends on non-uniform value
+  if ((non_uniform_global == 42) || true) {
+                                 ^^
+
+test:7:8 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  if ((non_uniform_global == 42) || true) {
+       ^^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_ShortCircuitingCondition_NonUniformRHS_Or) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+var<private> p : i32;
+
+fn main() {
+  if (true || (non_uniform_global == 42)) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (true || (non_uniform_global == 42)) {
+  ^^
+
+test:7:16 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  if (true || (non_uniform_global == 42)) {
+               ^^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_NonUniformCondition_BarrierInElseIfBlock) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  if (non_uniform == 42) {
+  } else if (true) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (non_uniform == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  if (non_uniform == 42) {
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_VarBecomesNonUniform_BeforeCondition) {
+    // Use a function-scope variable for control-flow guarding a barrier, and then assign to that
+    // variable before checking the condition.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var v = 0;
+  v = rw;
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:6:7 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  v = rw;
+      ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_VarBecomesNonUniform_AfterCondition) {
+    // Use a function-scope variable for control-flow guarding a barrier, and then assign to that
+    // variable after checking the condition.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var v = 0;
+  if (v == 0) {
+    v = rw;
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_VarBecomesNonUniformInIf_BarrierInElse) {
+    // Assign a non-uniform value to a variable in an if-block, and then use that variable for a
+    // conditional barrier in the else block.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  if (true) {
+    v = non_uniform;
+  } else {
+    if (v == 0) {
+      workgroupBarrier();
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_AssignNonUniformInIf_AssignUniformInElse) {
+    // Assign a non-uniform value to a variable in an if-block and a uniform value in the else
+    // block, and then use that variable for a conditional barrier after the if-else statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  if (true) {
+    if (true) {
+      v = non_uniform;
+    } else {
+      v = 5;
+    }
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:14:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:8:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+      v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_AssignNonUniformInIfWithReturn) {
+    // Assign a non-uniform value to a variable in an if-block followed by a return, and then use
+    // that variable for a conditional barrier after the if-else statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  if (true) {
+    v = non_uniform;
+    return;
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_AssignNonUniformBeforeIf_BothBranchesAssignUniform) {
+    // Assign a non-uniform value to a variable before and if-else statement, assign uniform values
+    // in both branch of the if-else, and then use that variable for a conditional barrier after
+    // the if-else statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  v = non_uniform;
+  if (true) {
+    v = 5;
+  } else {
+    v = 6;
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_AssignNonUniformBeforeIf_OnlyTrueBranchAssignsUniform) {
+    // Assign a non-uniform value to a variable before and if-else statement, assign a uniform value
+    // in the true branch of the if-else, and then use that variable for a conditional barrier after
+    // the if-else statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  v = non_uniform;
+  if (true) {
+    v = 5;
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:11:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:6:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  v = non_uniform;
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_AssignNonUniformBeforeIf_OnlyFalseBranchAssignsUniform) {
+    // Assign a non-uniform value to a variable before and if-else statement, assign a uniform value
+    // in the false branch of the if-else, and then use that variable for a conditional barrier
+    // after the if-else statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  v = non_uniform;
+  if (true) {
+  } else {
+    v = 5;
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:12:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:6:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  v = non_uniform;
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest,
+       IfElse_AssignNonUniformBeforeIf_OnlyTrueBranchAssignsUniform_FalseBranchReturns) {
+    // Assign a non-uniform value to a variable before and if-else statement, assign a uniform value
+    // in the true branch of the if-else, leave the variable untouched in the false branch and just
+    // return, and then use that variable for a conditional barrier after the if-else statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  v = non_uniform;
+  if (true) {
+    v = 5;
+  } else {
+    return;
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest,
+       IfElse_AssignNonUniformBeforeIf_OnlyFalseBranchAssignsUniform_TrueBranchReturns) {
+    // Assign a non-uniform value to a variable before and if-else statement, assign a uniform value
+    // in the false branch of the if-else, leave the variable untouched in the true branch and just
+    // return, and then use that variable for a conditional barrier after the if-else statement.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  v = non_uniform;
+  if (true) {
+    return;
+  } else {
+    v = 5;
+  }
+
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_NonUniformCondition_Reconverge) {
+    // If statements reconverge at exit, so test that we can call workgroupBarrier() after an if
+    // statement with a non-uniform condition.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  if (non_uniform == 42) {
+  } else {
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_ShortCircuitingNonUniformConditionLHS_Reconverge) {
+    // If statements reconverge at exit, so test that we can call workgroupBarrier() after an if
+    // statement with a non-uniform condition that uses short-circuiting.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  if (non_uniform == 42 || true) {
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_ShortCircuitingNonUniformConditionRHS_Reconverge) {
+    // If statements reconverge at exit, so test that we can call workgroupBarrier() after an if
+    // statement with a non-uniform condition that uses short-circuiting.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  if (false && non_uniform == 42) {
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_NonUniformFunctionCall_Reconverge) {
+    // If statements reconverge at exit, so test that we can call workgroupBarrier() after an if
+    // statement with a non-uniform condition.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar() {
+  if (non_uniform == 42) {
+    return;
+  } else {
+    return;
+  }
+}
+
+fn foo() {
+  if (non_uniform == 42) {
+    bar();
+  } else {
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_NonUniformReturn_NoReconverge) {
+    // If statements should not reconverge after non-uniform returns.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  if (non_uniform == 42) {
+    return;
+  } else {
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (non_uniform == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  if (non_uniform == 42) {
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, IfElse_NonUniformDiscard_NoReconverge) {
+    // If statements should not reconverge after non-uniform discards.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  if (non_uniform == 42) {
+    discard;
+  } else {
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (non_uniform == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  if (non_uniform == 42) {
+      ^^^^^^^^^^^
+)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Switch statement tests.
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(UniformityAnalysisTest, Switch_NonUniformCondition_BarrierInCase) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  switch (non_uniform) {
+    case 42: {
+      workgroupBarrier();
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  switch (non_uniform) {
+  ^^^^^^
+
+test:5:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  switch (non_uniform) {
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NonUniformCondition_BarrierInDefault) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  switch (non_uniform) {
+    default: {
+      workgroupBarrier();
+      break;
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  switch (non_uniform) {
+  ^^^^^^
+
+test:5:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  switch (non_uniform) {
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NonUniformBreak) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  switch (condition) {
+    case 42: {
+      if (non_uniform == 42) {
+        break;
+      }
+      workgroupBarrier();
+    }
+    default: {
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:11:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:8:7 note: control flow depends on non-uniform value
+      if (non_uniform == 42) {
+      ^^
+
+test:8:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+      if (non_uniform == 42) {
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NonUniformBreakInDifferentCase) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  switch (condition) {
+    case 0: {
+      if (non_uniform == 42) {
+        break;
+      }
+    }
+    case 42: {
+      workgroupBarrier();
+    }
+    default: {
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NonUniformBreakInDifferentCase_Fallthrough) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  switch (condition) {
+    case 0: {
+      if (non_uniform == 42) {
+        break;
+      }
+      fallthrough;
+    }
+    case 42: {
+      workgroupBarrier();
+    }
+    default: {
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:8:7 note: control flow depends on non-uniform value
+      if (non_uniform == 42) {
+      ^^
+
+test:8:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+      if (non_uniform == 42) {
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_VarBecomesNonUniformInDifferentCase_WithBreak) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = 0;
+  switch (condition) {
+    case 0: {
+      x = non_uniform;
+      break;
+    }
+    case 42: {
+      if (x == 0) {
+        workgroupBarrier();
+      }
+    }
+    default: {
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Switch_VarBecomesNonUniformInDifferentCase_WithFallthrough) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = 0;
+  switch (condition) {
+    case 0: {
+      x = non_uniform;
+      fallthrough;
+    }
+    case 42: {
+      if (x == 0) {
+        workgroupBarrier();
+      }
+    }
+    default: {
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:9 warning: 'workgroupBarrier' must only be called from uniform control flow
+        workgroupBarrier();
+        ^^^^^^^^^^^^^^^^
+
+test:13:7 note: control flow depends on non-uniform value
+      if (x == 0) {
+      ^^
+
+test:9:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+      x = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_VarBecomesUniformInDifferentCase_WithBreak) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = non_uniform;
+  switch (condition) {
+    case 0: {
+      x = 5;
+      break;
+    }
+    case 42: {
+      if (x == 0) {
+        workgroupBarrier();
+      }
+    }
+    default: {
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:9 warning: 'workgroupBarrier' must only be called from uniform control flow
+        workgroupBarrier();
+        ^^^^^^^^^^^^^^^^
+
+test:13:7 note: control flow depends on non-uniform value
+      if (x == 0) {
+      ^^
+
+test:6:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var x = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_VarBecomesUniformInDifferentCase_WithFallthrough) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = non_uniform;
+  switch (condition) {
+    case 0: {
+      x = 5;
+      fallthrough;
+    }
+    case 42: {
+      if (x == 0) {
+        workgroupBarrier();
+      }
+    }
+    default: {
+    }
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Switch_VarBecomesNonUniformInCase_BarrierAfter) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = 0;
+  switch (condition) {
+    case 0: {
+      x = non_uniform;
+    }
+    case 42: {
+      x = 5;
+    }
+    default: {
+      x = 6;
+    }
+  }
+  if (x == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:19:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:18:3 note: control flow depends on non-uniform value
+  if (x == 0) {
+  ^^
+
+test:9:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+      x = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_VarBecomesUniformInAllCases_BarrierAfter) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = non_uniform;
+  switch (condition) {
+    case 0: {
+      x = 4;
+    }
+    case 42: {
+      x = 5;
+    }
+    default: {
+      x = 6;
+    }
+  }
+  if (x == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Switch_VarBecomesUniformInSomeCases_BarrierAfter) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = non_uniform;
+  switch (condition) {
+    case 0: {
+      x = 4;
+    }
+    case 42: {
+    }
+    default: {
+      x = 6;
+    }
+  }
+  if (x == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:17:3 note: control flow depends on non-uniform value
+  if (x == 0) {
+  ^^
+
+test:6:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var x = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_VarBecomesUniformInCasesThatDontReturn_BarrierAfter) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = non_uniform;
+  switch (condition) {
+    case 0: {
+      x = 4;
+    }
+    case 42: {
+      return;
+    }
+    default: {
+      x = 6;
+    }
+  }
+  if (x == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Switch_VarBecomesUniformAfterConditionalBreak_BarrierAfter) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = non_uniform;
+  switch (condition) {
+    case 0: {
+      x = 4;
+    }
+    case 42: {
+    }
+    default: {
+      if (false) {
+        break;
+      }
+      x = 6;
+    }
+  }
+  if (x == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:20:3 note: control flow depends on non-uniform value
+  if (x == 0) {
+  ^^
+
+test:6:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var x = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NestedInLoop_VarBecomesNonUniformWithBreak_BarrierInLoop) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = 0;
+  loop {
+    if (x == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    switch (condition) {
+      case 0: {
+        x = non_uniform;
+        break;
+      }
+      default: {
+        x = 6;
+      }
+    }
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:8:5 note: control flow depends on non-uniform value
+    if (x == 0) {
+    ^^
+
+test:15:13 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+        x = non_uniform;
+            ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NestedInLoop_VarBecomesNonUniformWithBreak_BarrierAfterLoop) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(0) var<uniform> condition : i32;
+
+fn foo() {
+  var x = 0;
+  loop {
+    if (false) {
+      break;
+    }
+    switch (condition) {
+      case 0: {
+        x = non_uniform;
+        break;
+      }
+      default: {
+        x = 6;
+      }
+    }
+    x = 5;
+  }
+  if (x == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NonUniformCondition_Reconverge) {
+    // Switch statements reconverge at exit, so test that we can call workgroupBarrier() after a
+    // switch statement that contains a non-uniform conditional break.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  switch (non_uniform) {
+    default: {
+      break;
+    }
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NonUniformBreak_Reconverge) {
+    // Switch statements reconverge at exit, so test that we can call workgroupBarrier() after a
+    // switch statement that contains a non-uniform conditional break.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  switch (42) {
+    default: {
+      if (non_uniform == 0) {
+        break;
+      }
+      break;
+    }
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NonUniformFunctionCall_Reconverge) {
+    // Switch statements reconverge at exit, so test that we can call workgroupBarrier() after a
+    // switch statement that contains a call to a function that causes non-uniform control flow.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn bar() {
+  if (n == 42) {
+    return;
+  } else {
+    return;
+  }
+}
+
+fn foo() {
+  switch (42) {
+    default: {
+      bar();
+      break;
+    }
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, Switch_NonUniformFunctionDiscard_NoReconvergence) {
+    // Switch statements should not reconverge after non-uniform discards.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> n : i32;
+
+fn bar() {
+  if (n == 42) {
+    discard;
+  }
+}
+
+fn foo() {
+  switch (42) {
+    default: {
+      bar();
+      break;
+    }
+  }
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:17:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:13:7 note: calling 'bar' may cause subsequent control flow to be non-uniform
+      bar();
+      ^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (n == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'n' may result in a non-uniform value
+  if (n == 42) {
+      ^
+)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Pointer tests.
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(UniformityAnalysisTest, AssignNonUniformThroughPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  *&v = non_uniform;
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:6:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  *&v = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, AssignNonUniformThroughCapturedPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  let pv = &v;
+  *pv = non_uniform;
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:7:9 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  *pv = non_uniform;
+        ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, AssignUniformThroughPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = non_uniform;
+  *&v = 42;
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, AssignUniformThroughCapturedPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = non_uniform;
+  let pv = &v;
+  *pv = 42;
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, AssignUniformThroughCapturedPointer_InNonUniformControlFlow) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  let pv = &v;
+  if (non_uniform == 0) {
+    *pv = 42;
+  }
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (non_uniform == 0) {
+  ^^
+
+test:7:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  if (non_uniform == 0) {
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, LoadNonUniformThroughPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = non_uniform;
+  if (*&v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:6:3 note: control flow depends on non-uniform value
+  if (*&v == 0) {
+  ^^
+
+test:5:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, LoadNonUniformThroughCapturedPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = non_uniform;
+  let pv = &v;
+  if (*pv == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (*pv == 0) {
+  ^^
+
+test:5:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, LoadNonUniformThroughPointerParameter) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>) {
+  if (*p == 0) {
+    workgroupBarrier();
+  }
+}
+
+fn foo() {
+  var v = non_uniform;
+  bar(&v);
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:12:7 warning: parameter 'p' of 'bar' must be uniform
+  bar(&v);
+      ^
+
+test:6:5 note: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:11:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, LoadUniformThroughPointer) {
+    std::string src = R"(
+fn foo() {
+  var v = 42;
+  if (*&v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, LoadUniformThroughCapturedPointer) {
+    std::string src = R"(
+fn foo() {
+  var v = 42;
+  let pv = &v;
+  if (*pv == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, LoadUniformThroughPointerParameter) {
+    std::string src = R"(
+fn bar(p : ptr<function, i32>) {
+  if (*p == 0) {
+    workgroupBarrier();
+  }
+}
+
+fn foo() {
+  var v = 42;
+  bar(&v);
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, StoreNonUniformAfterCapturingPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  let pv = &v;
+  v = non_uniform;
+  if (*pv == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (*pv == 0) {
+  ^^
+
+test:7:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  v = non_uniform;
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, StoreUniformAfterCapturingPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = non_uniform;
+  let pv = &v;
+  v = 42;
+  if (*pv == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, AssignNonUniformThroughLongChainOfPointers) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  let pv1 = &*&v;
+  let pv2 = &*&*pv1;
+  *&*&*pv2 = non_uniform;
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:9:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:8:14 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  *&*&*pv2 = non_uniform;
+             ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, LoadNonUniformThroughLongChainOfPointers) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = non_uniform;
+  let pv1 = &*&v;
+  let pv2 = &*&*pv1;
+  if (*&*&*pv2 == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (*&*&*pv2 == 0) {
+  ^^
+
+test:5:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, AssignUniformThenNonUniformThroughDifferentPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  let pv1 = &v;
+  let pv2 = &v;
+  *pv1 = 42;
+  *pv2 = non_uniform;
+  if (*pv1 == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:10:3 note: control flow depends on non-uniform value
+  if (*pv1 == 0) {
+  ^^
+
+test:9:10 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  *pv2 = non_uniform;
+         ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, AssignNonUniformThenUniformThroughDifferentPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  var v = 0;
+  let pv1 = &v;
+  let pv2 = &v;
+  *pv1 = non_uniform;
+  *pv2 = 42;
+  if (*pv1 == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, UnmodifiedPointerParameterNonUniform) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>) {
+}
+
+fn foo() {
+  var v = non_uniform;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:10:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:8:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, UnmodifiedPointerParameterUniform) {
+    std::string src = R"(
+fn bar(p : ptr<function, i32>) {
+}
+
+fn foo() {
+  var v = 42;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, AssignNonUniformThroughPointerInFunctionCall) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>) {
+  *p = non_uniform;
+}
+
+fn foo() {
+  var v = 0;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:11:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:10:7 note: pointer contents may become non-uniform after calling 'bar'
+  bar(&v);
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, AssignUniformThroughPointerInFunctionCall) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>) {
+  *p = 42;
+}
+
+fn foo() {
+  var v = non_uniform;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, AssignNonUniformThroughPointerInFunctionCallViaArg) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>, a : i32) {
+  *p = a;
+}
+
+fn foo() {
+  var v = 0;
+  bar(&v, non_uniform);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:11:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:10:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  bar(&v, non_uniform);
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, AssignNonUniformThroughPointerInFunctionCallViaPointerArg) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>, a : ptr<function, i32>) {
+  *p = *a;
+}
+
+fn foo() {
+  var v = 0;
+  var a = non_uniform;
+  bar(&v, &a);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:12:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:10:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var a = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, AssignUniformThroughPointerInFunctionCallViaArg) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>, a : i32) {
+  *p = a;
+}
+
+fn foo() {
+  var v = non_uniform;
+  bar(&v, 42);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, AssignUniformThroughPointerInFunctionCallViaPointerArg) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>, a : ptr<function, i32>) {
+  *p = *a;
+}
+
+fn foo() {
+  var v = non_uniform;
+  var a = 42;
+  bar(&v, &a);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, AssignNonUniformThroughPointerInFunctionCallChain) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn f3(p : ptr<function, i32>, a : ptr<function, i32>) {
+  *p = *a;
+}
+
+fn f2(p : ptr<function, i32>, a : ptr<function, i32>) {
+  f3(p, a);
+}
+
+fn f1(p : ptr<function, i32>, a : ptr<function, i32>) {
+  f2(p, a);
+}
+
+fn foo() {
+  var v = 0;
+  var a = non_uniform;
+  f1(&v, &a);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:20:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:18:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var a = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, AssignUniformThroughPointerInFunctionCallChain) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn f3(p : ptr<function, i32>, a : ptr<function, i32>) {
+  *p = *a;
+}
+
+fn f2(p : ptr<function, i32>, a : ptr<function, i32>) {
+  f3(p, a);
+}
+
+fn f1(p : ptr<function, i32>, a : ptr<function, i32>) {
+  f2(p, a);
+}
+
+fn foo() {
+  var v = non_uniform;
+  var a = 42;
+  f1(&v, &a);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, MakePointerParamUniformInReturnExpression) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn zoo(p : ptr<function, i32>) -> i32 {
+  *p = 5;
+  return 6;
+}
+
+fn bar(p : ptr<function, i32>) -> i32 {
+  *p = non_uniform;
+  return zoo(p);
+}
+
+fn foo() {
+  var v = 0;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, MakePointerParamNonUniformInReturnExpression) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn zoo(p : ptr<function, i32>) -> i32 {
+  *p = non_uniform;
+  return 6;
+}
+
+fn bar(p : ptr<function, i32>) -> i32 {
+  *p = 5;
+  return zoo(p);
+}
+
+fn foo() {
+  var v = 0;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:17:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:16:7 note: pointer contents may become non-uniform after calling 'bar'
+  bar(&v);
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, PointerParamAssignNonUniformInTrueAndUniformInFalse) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>) {
+  if (true) {
+    *p = non_uniform;
+  } else {
+    *p = 5;
+  }
+}
+
+fn foo() {
+  var v = 0;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:15:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:14:7 note: pointer contents may become non-uniform after calling 'bar'
+  bar(&v);
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ConditionalAssignNonUniformToPointerParamAndReturn) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>) {
+  if (true) {
+    *p = non_uniform;
+    return;
+  }
+  *p = 5;
+}
+
+fn foo() {
+  var v = 0;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:15:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:14:7 note: pointer contents may become non-uniform after calling 'bar'
+  bar(&v);
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ConditionalAssignNonUniformToPointerParamAndBreakFromSwitch) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(1) var<uniform> condition : i32;
+
+fn bar(p : ptr<function, i32>) {
+  switch (condition) {
+    case 0 {
+      if (true) {
+        *p = non_uniform;
+        break;
+      }
+      *p = 5;
+    }
+    default {
+      *p = 6;
+    }
+  }
+}
+
+fn foo() {
+  var v = 0;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:24:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:23:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:22:7 note: pointer contents may become non-uniform after calling 'bar'
+  bar(&v);
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ConditionalAssignNonUniformToPointerParamAndBreakFromLoop) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>) {
+  loop {
+    if (true) {
+      *p = non_uniform;
+      break;
+    }
+    *p = 5;
+  }
+}
+
+fn foo() {
+  var v = 0;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:17:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:16:7 note: pointer contents may become non-uniform after calling 'bar'
+  bar(&v);
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ConditionalAssignNonUniformToPointerParamAndContinue) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo(p : ptr<function, i32>) {
+  loop {
+    if (*p == 0) {
+      workgroupBarrier();
+      break;
+    }
+
+    if (true) {
+      *p = non_uniform;
+      continue;
+    }
+    *p = 5;
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+      workgroupBarrier();
+      ^^^^^^^^^^^^^^^^
+
+test:6:5 note: control flow depends on non-uniform value
+    if (*p == 0) {
+    ^^
+
+test:12:12 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+      *p = non_uniform;
+           ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, PointerParamMaybeBecomesUniform) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>) {
+  if (true) {
+    *p = 5;
+    return;
+  }
+}
+
+fn foo() {
+  var v = non_uniform;
+  bar(&v);
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:14:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:12:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, NonUniformPointerParameterBecomesUniform_AfterUse) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(a : ptr<function, i32>, b : ptr<function, i32>) {
+  *b = *a;
+  *a = 0;
+}
+
+fn foo() {
+  var a = non_uniform;
+  var b = 0;
+  bar(&a, &b);
+  if (b == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:13:3 note: control flow depends on non-uniform value
+  if (b == 0) {
+  ^^
+
+test:10:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var a = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, NonUniformPointerParameterBecomesUniform_BeforeUse) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(a : ptr<function, i32>, b : ptr<function, i32>) {
+  *a = 0;
+  *b = *a;
+}
+
+fn foo() {
+  var a = non_uniform;
+  var b = 0;
+  bar(&a, &b);
+  if (b == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, UniformPointerParameterBecomesNonUniform_BeforeUse) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(a : ptr<function, i32>, b : ptr<function, i32>) {
+  *a = non_uniform;
+  *b = *a;
+}
+
+fn foo() {
+  var a = 0;
+  var b = 0;
+  bar(&a, &b);
+  if (b == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:13:3 note: control flow depends on non-uniform value
+  if (b == 0) {
+  ^^
+
+test:12:11 note: pointer contents may become non-uniform after calling 'bar'
+  bar(&a, &b);
+          ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, UniformPointerParameterBecomesNonUniform_AfterUse) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(a : ptr<function, i32>, b : ptr<function, i32>) {
+  *b = *a;
+  *a = non_uniform;
+}
+
+fn foo() {
+  var a = 0;
+  var b = 0;
+  bar(&a, &b);
+  if (b == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, NonUniformPointerParameterUpdatedInPlace) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(p : ptr<function, i32>) {
+  (*p)++;
+}
+
+fn foo() {
+  var v = non_uniform;
+  bar(&v);
+  if (v == 1) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:11:3 note: control flow depends on non-uniform value
+  if (v == 1) {
+  ^^
+
+test:9:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var v = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, MultiplePointerParametersBecomeNonUniform) {
+    // The analysis traverses the tree for each pointer parameter, and we need to make sure that we
+    // reset the "visited" state of nodes in between these traversals to properly capture each of
+    // their uniformity states.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(a : ptr<function, i32>, b : ptr<function, i32>) {
+  *a = non_uniform;
+  *b = non_uniform;
+}
+
+fn foo() {
+  var a = 0;
+  var b = 0;
+  bar(&a, &b);
+  if (b == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:13:3 note: control flow depends on non-uniform value
+  if (b == 0) {
+  ^^
+
+test:12:11 note: pointer contents may become non-uniform after calling 'bar'
+  bar(&a, &b);
+          ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, MultiplePointerParametersWithEdgesToEachOther) {
+    // The analysis traverses the tree for each pointer parameter, and we need to make sure that we
+    // reset the "visited" state of nodes in between these traversals to properly capture each of
+    // their uniformity states.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar(a : ptr<function, i32>, b : ptr<function, i32>, c : ptr<function, i32>) {
+  *a = *a;
+  *b = *b;
+  *c = *a + *b;
+}
+
+fn foo() {
+  var a = non_uniform;
+  var b = 0;
+  var c = 0;
+  bar(&a, &b, &c);
+  if (c == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:15:3 note: control flow depends on non-uniform value
+  if (c == 0) {
+  ^^
+
+test:11:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  var a = non_uniform;
+          ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, MaximumNumberOfPointerParameters) {
+    // Create a function with the maximum number of parameters, all pointers, to stress the
+    // quadratic nature of the analysis.
+    ProgramBuilder b;
+    auto& ty = b.ty;
+
+    // fn foo(p0 : ptr<function, i32>, p1 : ptr<function, i32>, ...) {
+    //   let rhs = *p0 + *p1 + ... + *p244;
+    //   *p1 = rhs;
+    //   *p2 = rhs;
+    //   ...
+    //   *p254 = rhs;
+    // }
+    ast::VariableList params;
+    ast::StatementList foo_body;
+    const ast::Expression* rhs_init = b.Deref("p0");
+    for (int i = 1; i < 255; i++) {
+        rhs_init = b.Add(rhs_init, b.Deref("p" + std::to_string(i)));
+    }
+    foo_body.push_back(b.Decl(b.Let("rhs", nullptr, rhs_init)));
+    for (int i = 0; i < 255; i++) {
+        params.push_back(
+            b.Param("p" + std::to_string(i), ty.pointer(ty.i32(), ast::StorageClass::kFunction)));
+        if (i > 0) {
+            foo_body.push_back(b.Assign(b.Deref("p" + std::to_string(i)), "rhs"));
+        }
+    }
+    b.Func("foo", std::move(params), ty.void_(), foo_body);
+
+    // var<private> non_uniform_global : i32;
+    // fn main() {
+    //   var v0 : i32;
+    //   var v1 : i32;
+    //   ...
+    //   var v254 : i32;
+    //   v0 = non_uniform_global;
+    //   foo(&v0, &v1, ...,  &v254);
+    //   if (v254 == 0) {
+    //     workgroupBarrier();
+    //   }
+    // }
+    b.Global("non_uniform_global", ty.i32(), ast::StorageClass::kPrivate);
+    ast::StatementList main_body;
+    ast::ExpressionList args;
+    for (int i = 0; i < 255; i++) {
+        auto name = "v" + std::to_string(i);
+        main_body.push_back(b.Decl(b.Var(name, ty.i32())));
+        args.push_back(b.AddressOf(name));
+    }
+    main_body.push_back(b.Assign("v0", "non_uniform_global"));
+    main_body.push_back(b.CallStmt(b.create<ast::CallExpression>(b.Expr("foo"), args)));
+    main_body.push_back(
+        b.If(b.Equal("v254", 0_i), b.Block(b.CallStmt(b.Call("workgroupBarrier")))));
+    b.Func("main", {}, ty.void_(), main_body);
+
+    // TODO(jrprice): Expect false when uniformity issues become errors.
+    EXPECT_TRUE(RunTest(std::move(b))) << error_;
+    EXPECT_EQ(error_,
+              R"(warning: 'workgroupBarrier' must only be called from uniform control flow
+note: control flow depends on non-uniform value
+note: reading from module-scope private variable 'non_uniform_global' may result in a non-uniform value)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Tests to cover access to aggregate types.
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(UniformityAnalysisTest, VectorElement_Uniform) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> v : vec4<i32>;
+
+fn foo() {
+  if (v[2] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, VectorElement_NonUniform) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> v : array<i32>;
+
+fn foo() {
+  if (v[2] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (v[2] == 0) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'v' may result in a non-uniform value
+  if (v[2] == 0) {
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, VectorElement_BecomesNonUniform_BeforeCondition) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var v : vec4<i32>;
+  v[2] = rw;
+  if (v[2] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (v[2] == 0) {
+  ^^
+
+test:6:10 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  v[2] = rw;
+         ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, VectorElement_BecomesNonUniform_AfterCondition) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var v : vec4<i32>;
+  if (v[2] == 0) {
+    v[2] = rw;
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, VectorElement_DifferentElementBecomesNonUniform) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var v : vec4<i32>;
+  v[1] = rw;
+  if (v[2] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (v[2] == 0) {
+  ^^
+
+test:6:10 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  v[1] = rw;
+         ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, VectorElement_ElementBecomesUniform) {
+    // For aggregate types, we conservatively consider them to be forever non-uniform once they
+    // become non-uniform. Test that after assigning a uniform value to an element, that element is
+    // still considered to be non-uniform.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var v : vec4<i32>;
+  v[1] = rw;
+  v[1] = 42;
+  if (v[1] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (v[1] == 0) {
+  ^^
+
+test:6:10 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  v[1] = rw;
+         ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, VectorElement_DifferentElementBecomesUniform) {
+    // For aggregate types, we conservatively consider them to be forever non-uniform once they
+    // become non-uniform. Test that after assigning a uniform value to an element, the whole vector
+    // is still considered to be non-uniform.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var v : vec4<i32>;
+  v[1] = rw;
+  v[2] = 42;
+  if (v[1] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (v[1] == 0) {
+  ^^
+
+test:6:10 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  v[1] = rw;
+         ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, VectorElement_NonUniform_AnyBuiltin) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+fn foo() {
+  var v : vec4<i32>;
+  v[1] = non_uniform_global;
+  if (any(v == vec4(42))) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (any(v == vec4(42))) {
+  ^^
+
+test:6:10 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  v[1] = non_uniform_global;
+         ^^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, StructMember_Uniform) {
+    std::string src = R"(
+struct S {
+  a : i32,
+  b : i32,
+}
+@group(0) @binding(0) var<storage, read> s : S;
+
+fn foo() {
+  if (s.b == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, StructMember_NonUniform) {
+    std::string src = R"(
+struct S {
+  a : i32,
+  b : i32,
+}
+@group(0) @binding(0) var<storage, read_write> s : S;
+
+fn foo() {
+  if (s.b == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:9:3 note: control flow depends on non-uniform value
+  if (s.b == 0) {
+  ^^
+
+test:9:7 note: reading from read_write storage buffer 's' may result in a non-uniform value
+  if (s.b == 0) {
+      ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, StructMember_BecomesNonUniform_BeforeCondition) {
+    std::string src = R"(
+struct S {
+  a : i32,
+  b : i32,
+}
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var s : S;
+  s.b = rw;
+  if (s.b == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:11:3 note: control flow depends on non-uniform value
+  if (s.b == 0) {
+  ^^
+
+test:10:9 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  s.b = rw;
+        ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, StructMember_BecomesNonUniform_AfterCondition) {
+    std::string src = R"(
+struct S {
+  a : i32,
+  b : i32,
+}
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var s : S;
+  if (s.b == 0) {
+    s.b = rw;
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, StructMember_DifferentMemberBecomesNonUniform) {
+    std::string src = R"(
+struct S {
+  a : i32,
+  b : i32,
+}
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var s : S;
+  s.a = rw;
+  if (s.b == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:11:3 note: control flow depends on non-uniform value
+  if (s.b == 0) {
+  ^^
+
+test:10:9 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  s.a = rw;
+        ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, StructMember_MemberBecomesUniform) {
+    // For aggregate types, we conservatively consider them to be forever non-uniform once they
+    // become non-uniform. Test that after assigning a uniform value to a member, that member is
+    // still considered to be non-uniform.
+    std::string src = R"(
+struct S {
+  a : i32,
+  b : i32,
+}
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var s : S;
+  s.a = rw;
+  s.a = 0;
+  if (s.a == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:12:3 note: control flow depends on non-uniform value
+  if (s.a == 0) {
+  ^^
+
+test:10:9 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  s.a = rw;
+        ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, StructMember_DifferentMemberBecomesUniform) {
+    // For aggregate types, we conservatively consider them to be forever non-uniform once they
+    // become non-uniform. Test that after assigning a uniform value to a member, the whole struct
+    // is still considered to be non-uniform.
+    std::string src = R"(
+struct S {
+  a : i32,
+  b : i32,
+}
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var s : S;
+  s.a = rw;
+  s.b = 0;
+  if (s.a == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:12:3 note: control flow depends on non-uniform value
+  if (s.a == 0) {
+  ^^
+
+test:10:9 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  s.a = rw;
+        ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ArrayElement_Uniform) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> arr : array<i32>;
+
+fn foo() {
+  if (arr[7] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ArrayElement_NonUniform) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> arr : array<i32>;
+
+fn foo() {
+  if (arr[7] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (arr[7] == 0) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'arr' may result in a non-uniform value
+  if (arr[7] == 0) {
+      ^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ArrayElement_BecomesNonUniform_BeforeCondition) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var arr : array<i32, 4>;
+  arr[2] = rw;
+  if (arr[2] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (arr[2] == 0) {
+  ^^
+
+test:6:12 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  arr[2] = rw;
+           ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ArrayElement_BecomesNonUniform_AfterCondition) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var arr : array<i32, 4>;
+  if (arr[2] == 0) {
+    arr[2] = rw;
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ArrayElement_DifferentElementBecomesNonUniform) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var arr : array<i32, 4>;
+  arr[1] = rw;
+  if (arr[2] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (arr[2] == 0) {
+  ^^
+
+test:6:12 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  arr[1] = rw;
+           ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ArrayElement_DifferentElementBecomesNonUniformThroughPointer) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var arr : array<i32, 4>;
+  let pa = &arr[1];
+  *pa = rw;
+  if (arr[2] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (arr[2] == 0) {
+  ^^
+
+test:7:9 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  *pa = rw;
+        ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ArrayElement_ElementBecomesUniform) {
+    // For aggregate types, we conservatively consider them to be forever non-uniform once they
+    // become non-uniform. Test that after assigning a uniform value to an element, that element is
+    // still considered to be non-uniform.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var arr : array<i32, 4>;
+  arr[1] = rw;
+  arr[1] = 42;
+  if (arr[1] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (arr[1] == 0) {
+  ^^
+
+test:6:12 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  arr[1] = rw;
+           ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ArrayElement_DifferentElementBecomesUniform) {
+    // For aggregate types, we conservatively consider them to be forever non-uniform once they
+    // become non-uniform. Test that after assigning a uniform value to an element, the whole array
+    // is still considered to be non-uniform.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var arr : array<i32, 4>;
+  arr[1] = rw;
+  arr[2] = 42;
+  if (arr[1] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:8:3 note: control flow depends on non-uniform value
+  if (arr[1] == 0) {
+  ^^
+
+test:6:12 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  arr[1] = rw;
+           ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ArrayElement_ElementBecomesUniformThroughPointer) {
+    // For aggregate types, we conservatively consider them to be forever non-uniform once they
+    // become non-uniform. Test that after assigning a uniform value to an element through a
+    // pointer, the whole array is still considered to be non-uniform.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var arr : array<i32, 4>;
+  let pa = &arr[2];
+  arr[1] = rw;
+  *pa = 42;
+  if (arr[1] == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:9:3 note: control flow depends on non-uniform value
+  if (arr[1] == 0) {
+  ^^
+
+test:7:12 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  arr[1] = rw;
+           ^^
+)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Miscellaneous statement and expression tests.
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(UniformityAnalysisTest, FunctionRequiresUniformFlowAndCausesNonUniformFlow) {
+    // Test that a function that requires uniform flow and then causes non-uniform flow can be
+    // called without error.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+fn foo() {
+  _ = dpdx(0.5);
+
+  if (non_uniform_global == 0) {
+    discard;
+  }
+}
+
+@stage(fragment)
+fn main() {
+  foo();
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, TypeConstructor) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+fn foo() {
+  if (i32(non_uniform_global) == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (i32(non_uniform_global) == 0) {
+  ^^
+
+test:5:11 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  if (i32(non_uniform_global) == 0) {
+          ^^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Conversion) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+fn foo() {
+  if (f32(non_uniform_global) == 0.0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (f32(non_uniform_global) == 0.0) {
+  ^^
+
+test:5:11 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  if (f32(non_uniform_global) == 0.0) {
+          ^^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Bitcast) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+fn foo() {
+  if (bitcast<f32>(non_uniform_global) == 0.0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (bitcast<f32>(non_uniform_global) == 0.0) {
+  ^^
+
+test:5:20 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  if (bitcast<f32>(non_uniform_global) == 0.0) {
+                   ^^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, CompoundAssignment_NonUniformRHS) {
+    // Use compound assignment with a non-uniform RHS on a variable.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var v = 0;
+  v += rw;
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:6:8 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  v += rw;
+       ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, CompoundAssignment_UniformRHS_StillNonUniform) {
+    // Use compound assignment with a uniform RHS on a variable that is already non-uniform.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  var v = rw;
+  v += 1;
+  if (v == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:7:3 note: control flow depends on non-uniform value
+  if (v == 0) {
+  ^^
+
+test:5:11 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
+  var v = rw;
+          ^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, PhonyAssignment_LhsCausesNonUniformControlFlow) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> nonuniform_var : i32;
+
+fn bar() -> i32 {
+  if (nonuniform_var == 42) {
+    return 1;
+  } else {
+    return 2;
+  }
+}
+
+fn foo() {
+  _ = bar();
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:14:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:13:7 note: calling 'bar' may cause subsequent control flow to be non-uniform
+  _ = bar();
+      ^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (nonuniform_var == 42) {
+  ^^
+
+test:5:7 note: reading from read_write storage buffer 'nonuniform_var' may result in a non-uniform value
+  if (nonuniform_var == 42) {
+      ^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, ShortCircuiting_CausesNonUniformControlFlow) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+var<private> p : i32;
+
+fn main() {
+  let b = (non_uniform_global == 42) && false;
+  workgroupBarrier();
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:7:38 note: control flow depends on non-uniform value
+  let b = (non_uniform_global == 42) && false;
+                                     ^^
+
+test:7:12 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  let b = (non_uniform_global == 42) && false;
+           ^^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, DeadCode_AfterReturn) {
+    // Dead code after a return statement shouldn't cause uniformity errors.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  return;
+  if (non_uniform == 42) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, DeadCode_AfterDiscard) {
+    // Dead code after a discard statement shouldn't cause uniformity errors.
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  discard;
+  if (non_uniform == 42) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ArrayLength) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> arr : array<f32>;
+
+fn foo() {
+  for (var i = 0u; i < arrayLength(&arr); i++) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, WorkgroupAtomics) {
+    std::string src = R"(
+var<workgroup> a : atomic<i32>;
+
+fn foo() {
+  if (atomicAdd(&a, 1) == 1) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (atomicAdd(&a, 1) == 1) {
+  ^^
+
+test:5:18 note: reading from workgroup storage variable 'a' may result in a non-uniform value
+  if (atomicAdd(&a, 1) == 1) {
+                 ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, StorageAtomics) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> a : atomic<i32>;
+
+fn foo() {
+  if (atomicAdd(&a, 1) == 1) {
+    storageBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:6:5 warning: 'storageBarrier' must only be called from uniform control flow
+    storageBarrier();
+    ^^^^^^^^^^^^^^
+
+test:5:3 note: control flow depends on non-uniform value
+  if (atomicAdd(&a, 1) == 1) {
+  ^^
+
+test:5:18 note: reading from read_write storage buffer 'a' may result in a non-uniform value
+  if (atomicAdd(&a, 1) == 1) {
+                 ^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, DisableAnalysisWithExtension) {
+    std::string src = R"(
+enable chromium_disable_uniformity_analysis;
+
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+fn foo() {
+  if (rw == 0) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, StressGraphTraversalDepth) {
+    // Create a function with a very long sequence of variable declarations and assignments to
+    // test traversals of very deep graphs. This requires a non-recursive traversal algorithm.
+    ProgramBuilder b;
+    auto& ty = b.ty;
+
+    // var<private> v0 : i32 = 0i;
+    // fn foo() {
+    //   let v1 = v0;
+    //   let v2 = v1;
+    //   ...
+    //   let v{N} = v{N-1};
+    //   if (v{N} == 0) {
+    //     workgroupBarrier();
+    //   }
+    // }
+    b.Global("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++) {
+        auto v = "v" + std::to_string(i);
+        foo_body.push_back(b.Decl(b.Var(v, nullptr, b.Expr(v_last))));
+        v_last = v;
+    }
+    foo_body.push_back(b.If(b.Equal(v_last, 0_i), b.Block(b.CallStmt(b.Call("workgroupBarrier")))));
+    b.Func("foo", {}, ty.void_(), foo_body);
+
+    // TODO(jrprice): Expect false when uniformity issues become errors.
+    EXPECT_TRUE(RunTest(std::move(b))) << error_;
+    EXPECT_EQ(error_,
+              R"(warning: 'workgroupBarrier' must only be called from uniform control flow
+note: control flow depends on non-uniform value
+note: reading from module-scope private variable 'v0' may result in a non-uniform value)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Tests for the quality of the error messages produced by the analysis.
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(UniformityAnalysisTest, Error_CallUserThatCallsBuiltinDirectly) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+  workgroupBarrier();
+}
+
+fn main() {
+  if (non_uniform == 42) {
+    foo();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:10:5 warning: 'foo' must only be called from uniform control flow
+    foo();
+    ^^^
+
+test:5:3 note: 'foo' requires uniformity because it calls workgroupBarrier
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:9:3 note: control flow depends on non-uniform value
+  if (non_uniform == 42) {
+  ^^
+
+test:9:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  if (non_uniform == 42) {
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Error_CallUserThatCallsBuiltinIndirectly) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn zoo() {
+  workgroupBarrier();
+}
+
+fn bar() {
+  zoo();
+}
+
+fn foo() {
+  bar();
+}
+
+fn main() {
+  if (non_uniform == 42) {
+    foo();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:18:5 warning: 'foo' must only be called from uniform control flow
+    foo();
+    ^^^
+
+test:5:3 note: 'foo' requires uniformity because it indirectly calls workgroupBarrier
+  workgroupBarrier();
+  ^^^^^^^^^^^^^^^^
+
+test:17:3 note: control flow depends on non-uniform value
+  if (non_uniform == 42) {
+  ^^
+
+test:17:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  if (non_uniform == 42) {
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Error_ParametersRequireUniformityInChain) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn zoo(a : i32) {
+  if (a == 42) {
+    workgroupBarrier();
+  }
+}
+
+fn bar(b : i32) {
+  zoo(b);
+}
+
+fn foo(c : i32) {
+  bar(c);
+}
+
+fn main() {
+  foo(non_uniform);
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:19:7 warning: parameter 'c' of 'foo' must be uniform
+  foo(non_uniform);
+      ^^^^^^^^^^^
+
+test:15:7 note: parameter 'b' of 'bar' must be uniform
+  bar(c);
+      ^
+
+test:11:7 note: parameter 'a' of 'zoo' must be uniform
+  zoo(b);
+      ^
+
+test:6:5 note: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:19:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+  foo(non_uniform);
+      ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Error_ReturnValueMayBeNonUniformChain) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn zoo() -> i32 {
+  return non_uniform;
+}
+
+fn bar() -> i32 {
+  return zoo();
+}
+
+fn foo() -> i32 {
+  return bar();
+}
+
+fn main() {
+  if (foo() == 42) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:17:3 note: control flow depends on non-uniform value
+  if (foo() == 42) {
+  ^^
+
+test:17:7 note: return value of 'foo' may be non-uniform
+  if (foo() == 42) {
+      ^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Error_SubsequentControlFlowMayBeNonUniform) {
+    // Make sure we correctly identify the function call as the source of non-uniform control flow
+    // and not the if statement with the uniform condition.
+    std::string src = R"(
+@group(0) @binding(0) var<uniform> uniform_value : i32;
+@group(0) @binding(1) var<storage, read_write> non_uniform_value : i32;
+
+fn foo() -> i32 {
+  if (non_uniform_value == 0) {
+    return 5;
+  }
+  return 6;
+}
+
+fn main() {
+  foo();
+  if (uniform_value == 42) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:13:3 note: calling 'foo' may cause subsequent control flow to be non-uniform
+  foo();
+  ^^^
+
+test:6:3 note: control flow depends on non-uniform value
+  if (non_uniform_value == 0) {
+  ^^
+
+test:6:7 note: reading from read_write storage buffer 'non_uniform_value' may result in a non-uniform value
+  if (non_uniform_value == 0) {
+      ^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Error_ParameterRequiredToBeUniformForSubsequentControlFlow) {
+    // Make sure we correctly identify the function call as the source of non-uniform control flow
+    // and not the if statement with the uniform condition.
+    std::string src = R"(
+@group(0) @binding(0) var<uniform> uniform_value : i32;
+@group(0) @binding(1) var<storage, read_write> non_uniform_value : i32;
+
+fn foo(x : i32) -> i32 {
+  if (x == 0) {
+    return 5;
+  }
+  return 6;
+}
+
+fn main() {
+  foo(non_uniform_value);
+  if (uniform_value == 42) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:13:7 note: non-uniform function call argument causes subsequent control flow to be non-uniform
+  foo(non_uniform_value);
+      ^^^^^^^^^^^^^^^^^
+
+test:6:3 note: control flow depends on non-uniform value
+  if (x == 0) {
+  ^^
+
+test:6:7 note: reading from 'x' may result in a non-uniform value
+  if (x == 0) {
+      ^
+
+test:13:7 note: reading from read_write storage buffer 'non_uniform_value' may result in a non-uniform value
+  foo(non_uniform_value);
+      ^^^^^^^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, Error_ShortCircuitingExprCausesNonUniformControlFlow) {
+    // Make sure we correctly identify the short-circuit as the source of non-uniform control flow
+    // and not the if statement with the uniform condition.
+    std::string src = R"(
+@group(0) @binding(0) var<uniform> uniform_value : i32;
+@group(0) @binding(1) var<storage, read_write> non_uniform_value : i32;
+
+fn main() {
+  let b = (non_uniform_value == 0) && true;
+  if (uniform_value == 42) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+    workgroupBarrier();
+    ^^^^^^^^^^^^^^^^
+
+test:6:36 note: control flow depends on non-uniform value
+  let b = (non_uniform_value == 0) && true;
+                                   ^^
+
+test:6:12 note: reading from read_write storage buffer 'non_uniform_value' may result in a non-uniform value
+  let b = (non_uniform_value == 0) && true;
+           ^^^^^^^^^^^^^^^^^
+)");
+}
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index f9b0ba7..9f698e6 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -194,7 +194,7 @@
 
 // https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
 bool Validator::IsHostShareable(const sem::Type* type) const {
-    if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
+    if (type->IsAnyOf<sem::I32, sem::U32, sem::F32, sem::F16>()) {
         return true;
     }
     return Switch(
@@ -652,6 +652,13 @@
         return false;
     }
 
+    if (auto* r = storage_ty->As<sem::SampledTexture>()) {
+        if (!r->type()->UnwrapRef()->is_numeric_scalar()) {
+            AddError("texture_2d<type>: type must be f32, i32 or u32", decl->source);
+            return false;
+        }
+    }
+
     if (auto* r = storage_ty->As<sem::MultisampledTexture>()) {
         if (r->dim() != ast::TextureDimension::k2d) {
             AddError("only 2d multisampled textures are supported", decl->source);
@@ -1356,6 +1363,35 @@
     return true;
 }
 
+bool Validator::Call(const sem::Call* call, sem::Statement* current_statement) const {
+    auto* expr = call->Declaration();
+    bool is_call_stmt =
+        current_statement && Is<ast::CallStatement>(current_statement->Declaration(),
+                                                    [&](auto* stmt) { return stmt->expr == expr; });
+
+    return Switch(
+        call->Target(),  //
+        [&](const sem::TypeConversion*) {
+            if (is_call_stmt) {
+                AddError("type conversion evaluated but not used", call->Declaration()->source);
+                return false;
+            }
+            return true;
+        },
+        [&](const sem::TypeConstructor* ctor) {
+            if (is_call_stmt) {
+                AddError("type constructor evaluated but not used", call->Declaration()->source);
+                return false;
+            }
+            return Switch(
+                ctor->ReturnType(),  //
+                [&](const sem::Array* arr) { return ArrayConstructor(expr, arr); },
+                [&](const sem::Struct* str) { return StructureConstructor(expr, str); },
+                [&](Default) { return true; });
+        },
+        [&](Default) { return true; });
+}
+
 bool Validator::DiscardStatement(const sem::Statement* stmt,
                                  sem::Statement* current_statement) const {
     if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
@@ -1486,7 +1522,7 @@
             if (is_const_expr) {
                 auto vector = builtin->Parameters()[index]->Type()->Is<sem::Vector>();
                 for (size_t i = 0; i < values.Elements().size(); i++) {
-                    auto value = values.Elements()[i].i32;
+                    auto value = values.Element<AInt>(i).value;
                     if (value < min || value > max) {
                         if (vector) {
                             AddError("each component of the " + name +
@@ -1517,21 +1553,22 @@
            check_arg_is_constexpr(sem::ParameterUsage::kComponent, 0, 3);
 }
 
-bool Validator::RequiredExtensionForBuiltinFunction(const sem::Call* call,
-                                                    const ast::ExtensionSet& extensionSet) const {
+bool Validator::RequiredExtensionForBuiltinFunction(
+    const sem::Call* call,
+    const ast::Extensions& enabled_extensions) const {
     const auto* builtin = call->Target()->As<sem::Builtin>();
     if (!builtin) {
         return true;
     }
 
     const auto extension = builtin->RequiredExtension();
-    if (extension == ast::Enable::ExtensionKind::kNotAnExtension) {
+    if (extension == ast::Extension::kNone) {
         return true;
     }
 
-    if (extensionSet.find(extension) == extensionSet.cend()) {
+    if (!enabled_extensions.contains(extension)) {
         AddError("cannot call built-in function '" + std::string(builtin->str()) +
-                     "' without extension " + ast::Enable::KindToName(extension),
+                     "' without extension " + ast::str(extension),
                  call->Declaration()->source);
         return false;
     }
@@ -1649,8 +1686,8 @@
     return true;
 }
 
-bool Validator::StructureConstructorOrCast(const ast::CallExpression* ctor,
-                                           const sem::Struct* struct_type) const {
+bool Validator::StructureConstructor(const ast::CallExpression* ctor,
+                                     const sem::Struct* struct_type) const {
     if (!struct_type->IsConstructible()) {
         AddError("struct constructor has non-constructible type", ctor->source);
         return false;
@@ -1682,8 +1719,8 @@
     return true;
 }
 
-bool Validator::ArrayConstructorOrCast(const ast::CallExpression* ctor,
-                                       const sem::Array* array_type) const {
+bool Validator::ArrayConstructor(const ast::CallExpression* ctor,
+                                 const sem::Array* array_type) const {
     auto& values = ctor->args;
     auto* elem_ty = array_type->ElemType();
     for (auto* value : values) {
@@ -1721,66 +1758,6 @@
     return true;
 }
 
-bool Validator::VectorConstructorOrCast(const ast::CallExpression* ctor,
-                                        const sem::Vector* vec_type) const {
-    auto& values = ctor->args;
-    auto* elem_ty = vec_type->type();
-    size_t value_cardinality_sum = 0;
-    for (auto* value : values) {
-        auto* value_ty = sem_.TypeOf(value)->UnwrapRef();
-        if (value_ty->is_scalar()) {
-            if (elem_ty != value_ty) {
-                AddError(
-                    "type in vector constructor does not match vector type: "
-                    "expected '" +
-                        sem_.TypeNameOf(elem_ty) + "', found '" + sem_.TypeNameOf(value_ty) + "'",
-                    value->source);
-                return false;
-            }
-
-            value_cardinality_sum++;
-        } else if (auto* value_vec = value_ty->As<sem::Vector>()) {
-            auto* value_elem_ty = value_vec->type();
-            // A mismatch of vector type parameter T is only an error if multiple
-            // arguments are present. A single argument constructor constitutes a
-            // type conversion expression.
-            if (elem_ty != value_elem_ty && values.size() > 1u) {
-                AddError(
-                    "type in vector constructor does not match vector type: "
-                    "expected '" +
-                        sem_.TypeNameOf(elem_ty) + "', found '" + sem_.TypeNameOf(value_elem_ty) +
-                        "'",
-                    value->source);
-                return false;
-            }
-
-            value_cardinality_sum += value_vec->Width();
-        } else {
-            // A vector constructor can only accept vectors and scalars.
-            AddError("expected vector or scalar type in vector constructor; found: " +
-                         sem_.TypeNameOf(value_ty),
-                     value->source);
-            return false;
-        }
-    }
-
-    // A correct vector constructor must either be a zero-value expression,
-    // a single-value initializer (splat) expression, or the number of components
-    // of all constructor arguments must add up to the vector cardinality.
-    if (value_cardinality_sum > 1 && value_cardinality_sum != vec_type->Width()) {
-        if (values.empty()) {
-            TINT_ICE(Resolver, diagnostics_) << "constructor arguments expected to be non-empty!";
-        }
-        const Source& values_start = values[0]->source;
-        const Source& values_end = values[values.size() - 1]->source;
-        AddError("attempted to construct '" + sem_.TypeNameOf(vec_type) + "' with " +
-                     std::to_string(value_cardinality_sum) + " component(s)",
-                 Source::Combine(values_start, values_end));
-        return false;
-    }
-    return true;
-}
-
 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);
@@ -1797,115 +1774,6 @@
     return true;
 }
 
-bool Validator::MatrixConstructorOrCast(const ast::CallExpression* ctor,
-                                        const sem::Matrix* matrix_ty) const {
-    auto& values = ctor->args;
-    // Zero Value expression
-    if (values.empty()) {
-        return true;
-    }
-
-    if (!Matrix(matrix_ty, ctor->source)) {
-        return false;
-    }
-
-    std::vector<const sem::Type*> arg_tys;
-    arg_tys.reserve(values.size());
-    for (auto* value : values) {
-        arg_tys.emplace_back(sem_.TypeOf(value)->UnwrapRef());
-    }
-
-    auto* elem_type = matrix_ty->type();
-    auto num_elements = matrix_ty->columns() * matrix_ty->rows();
-
-    // Print a generic error for an invalid matrix constructor, showing the
-    // available overloads.
-    auto print_error = [&]() {
-        const Source& values_start = values[0]->source;
-        const Source& values_end = values[values.size() - 1]->source;
-        auto type_name = sem_.TypeNameOf(matrix_ty);
-        auto elem_type_name = sem_.TypeNameOf(elem_type);
-        std::stringstream ss;
-        ss << "no matching constructor " + type_name << "(";
-        for (size_t i = 0; i < values.size(); i++) {
-            if (i > 0) {
-                ss << ", ";
-            }
-            ss << arg_tys[i]->FriendlyName(symbols_);
-        }
-        ss << ")" << std::endl << std::endl;
-        ss << "3 candidates available:" << std::endl;
-        ss << "  " << type_name << "()" << std::endl;
-        ss << "  " << type_name << "(" << elem_type_name << ",...," << elem_type_name << ")"
-           << " // " << std::to_string(num_elements) << " arguments" << std::endl;
-        ss << "  " << type_name << "(";
-        for (uint32_t c = 0; c < matrix_ty->columns(); c++) {
-            if (c > 0) {
-                ss << ", ";
-            }
-            ss << VectorPretty(matrix_ty->rows(), elem_type);
-        }
-        ss << ")" << std::endl;
-        AddError(ss.str(), Source::Combine(values_start, values_end));
-    };
-
-    const sem::Type* expected_arg_type = nullptr;
-    if (num_elements == values.size()) {
-        // Column-major construction from scalar elements.
-        expected_arg_type = matrix_ty->type();
-    } else if (matrix_ty->columns() == values.size()) {
-        // Column-by-column construction from vectors.
-        expected_arg_type = matrix_ty->ColumnType();
-    } else {
-        print_error();
-        return false;
-    }
-
-    for (auto* arg_ty : arg_tys) {
-        if (arg_ty != expected_arg_type) {
-            print_error();
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool Validator::ScalarConstructorOrCast(const ast::CallExpression* ctor,
-                                        const sem::Type* ty) const {
-    if (ctor->args.size() == 0) {
-        return true;
-    }
-    if (ctor->args.size() > 1) {
-        AddError(
-            "expected zero or one value in constructor, got " + std::to_string(ctor->args.size()),
-            ctor->source);
-        return false;
-    }
-
-    // Validate constructor
-    auto* value = ctor->args[0];
-    auto* value_ty = sem_.TypeOf(value)->UnwrapRef();
-
-    using Bool = sem::Bool;
-    using I32 = sem::I32;
-    using U32 = sem::U32;
-    using F32 = sem::F32;
-
-    const bool is_valid =
-        (ty->Is<Bool>() && value_ty->is_scalar()) || (ty->Is<I32>() && value_ty->is_scalar()) ||
-        (ty->Is<U32>() && value_ty->is_scalar()) || (ty->Is<F32>() && value_ty->is_scalar());
-    if (!is_valid) {
-        AddError("cannot construct '" + sem_.TypeNameOf(ty) + "' with a value of type '" +
-                     sem_.TypeNameOf(value_ty) + "'",
-                 ctor->source);
-
-        return false;
-    }
-
-    return true;
-}
-
 bool Validator::PipelineStages(const std::vector<sem::Function*>& entry_points) const {
     auto check_workgroup_storage = [&](const sem::Function* func,
                                        const sem::Function* entry_point) {
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 21c8e70..a8c18d5 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -183,6 +183,12 @@
     /// @returns true on success, false otherwise
     bool ContinueStatement(const sem::Statement* stmt, sem::Statement* current_statement) const;
 
+    /// Validates a call
+    /// @param call the call
+    /// @param current_statement the current statement being resolved
+    /// @returns true on success, false otherwise
+    bool Call(const sem::Call* call, sem::Statement* current_statement) const;
+
     /// Validates a discard statement
     /// @param stmt the statement to validate
     /// @param current_statement the current statement being resolved
@@ -308,12 +314,12 @@
     /// @returns true on success, false otherwise.
     bool Structure(const sem::Struct* str, ast::PipelineStage stage) const;
 
-    /// Validates a structure constructor or cast
+    /// Validates a structure constructor
     /// @param ctor the call expression to validate
     /// @param struct_type the type of the structure
     /// @returns true on success, false otherwise
-    bool StructureConstructorOrCast(const ast::CallExpression* ctor,
-                                    const sem::Struct* struct_type) const;
+    bool StructureConstructor(const ast::CallExpression* ctor,
+                              const sem::Struct* struct_type) const;
 
     /// Validates a switch statement
     /// @param s the switch to validate
@@ -342,31 +348,11 @@
     /// @returns true on success, false otherwise
     bool Vector(const sem::Vector* ty, const Source& source) const;
 
-    /// Validates a vector constructor or cast
-    /// @param ctor the call expression to validate
-    /// @param vec_type the vector type
-    /// @returns true on success, false otherwise
-    bool VectorConstructorOrCast(const ast::CallExpression* ctor,
-                                 const sem::Vector* vec_type) const;
-
-    /// Validates a matrix constructor or cast
-    /// @param ctor the call expression to validate
-    /// @param matrix_type the type of the matrix
-    /// @returns true on success, false otherwise
-    bool MatrixConstructorOrCast(const ast::CallExpression* ctor,
-                                 const sem::Matrix* matrix_type) const;
-
-    /// Validates a scalar constructor or cast
-    /// @param ctor the call expression to validate
-    /// @param type the type of the scalar
-    /// @returns true on success, false otherwise.
-    bool ScalarConstructorOrCast(const ast::CallExpression* ctor, const sem::Type* type) const;
-
-    /// Validates an array constructor or cast
+    /// Validates an array constructor
     /// @param ctor the call expresion to validate
     /// @param arr_type the type of the array
     /// @returns true on success, false otherwise
-    bool ArrayConstructorOrCast(const ast::CallExpression* ctor, const sem::Array* arr_type) const;
+    bool ArrayConstructor(const ast::CallExpression* ctor, const sem::Array* arr_type) const;
 
     /// Validates a texture builtin function
     /// @param call the builtin call to validate
@@ -375,10 +361,10 @@
 
     /// Validates an optional builtin function and its required extension.
     /// @param call the builtin call to validate
-    /// @param extensionSet all the extensions declared in current module
+    /// @param enabled_extensions all the extensions declared in current module
     /// @returns true on success, false otherwise
     bool RequiredExtensionForBuiltinFunction(const sem::Call* call,
-                                             const ast::ExtensionSet& extensionSet) const;
+                                             const ast::Extensions& enabled_extensions) const;
 
     /// Validates there are no duplicate attributes
     /// @param attributes the list of attributes to validate
diff --git a/src/tint/resolver/var_let_test.cc b/src/tint/resolver/var_let_test.cc
index 4c62bdd..4306736 100644
--- a/src/tint/resolver/var_let_test.cc
+++ b/src/tint/resolver/var_let_test.cc
@@ -86,7 +86,7 @@
     // struct S { i : i32; }
     // alias A = S;
     // fn F(){
-    //   var i : i32 = 1;
+    //   var i : i32 = 1i;
     //   var u : u32 = 1u;
     //   var f : f32 = 1.f;
     //   var b : bool = true;
diff --git a/src/tint/scope_stack.h b/src/tint/scope_stack.h
index 8bc97a3..6838f5b 100644
--- a/src/tint/scope_stack.h
+++ b/src/tint/scope_stack.h
@@ -24,7 +24,7 @@
 
 /// Used to store a stack of scope information.
 /// The stack starts with a global scope which can not be popped.
-template <class T>
+template <class K, class V>
 class ScopeStack {
   public:
     /// Constructor
@@ -47,32 +47,42 @@
     }
 
     /// Assigns the value into the top most scope of the stack.
-    /// @param symbol the symbol of the value
+    /// @param key the key of the value
     /// @param val the value
-    /// @returns the old value if there was an existing symbol at the top of the
+    /// @returns the old value if there was an existing key at the top of the
     /// stack, otherwise the zero initializer for type T.
-    T Set(const Symbol& symbol, T val) {
-        std::swap(val, stack_.back()[symbol]);
+    V Set(const K& key, V val) {
+        std::swap(val, stack_.back()[key]);
         return val;
     }
 
     /// Retrieves a value from the stack
-    /// @param symbol the symbol to look for
+    /// @param key the key to look for
     /// @returns the value, or the zero initializer if the value was not found
-    T Get(const Symbol& symbol) const {
+    V Get(const K& key) const {
         for (auto iter = stack_.rbegin(); iter != stack_.rend(); ++iter) {
             auto& map = *iter;
-            auto val = map.find(symbol);
+            auto val = map.find(key);
             if (val != map.end()) {
                 return val->second;
             }
         }
 
-        return T{};
+        return V{};
+    }
+
+    /// Return the top scope of the stack.
+    /// @returns the top scope of the stack
+    const std::unordered_map<K, V>& Top() const { return stack_.back(); }
+
+    /// Clear the scope stack.
+    void Clear() {
+        stack_.clear();
+        stack_.push_back({});
     }
 
   private:
-    std::vector<std::unordered_map<Symbol, T>> stack_;
+    std::vector<std::unordered_map<K, V>> stack_;
 };
 
 }  // namespace tint
diff --git a/src/tint/scope_stack_test.cc b/src/tint/scope_stack_test.cc
index 8062807..aeb7e73 100644
--- a/src/tint/scope_stack_test.cc
+++ b/src/tint/scope_stack_test.cc
@@ -22,7 +22,7 @@
 class ScopeStackTest : public ProgramBuilder, public testing::Test {};
 
 TEST_F(ScopeStackTest, Get) {
-    ScopeStack<uint32_t> s;
+    ScopeStack<Symbol, uint32_t> s;
     Symbol a(1, ID());
     Symbol b(3, ID());
     s.Push();
@@ -44,13 +44,13 @@
 }
 
 TEST_F(ScopeStackTest, Get_MissingSymbol) {
-    ScopeStack<uint32_t> s;
+    ScopeStack<Symbol, uint32_t> s;
     Symbol sym(1, ID());
     EXPECT_EQ(s.Get(sym), 0u);
 }
 
 TEST_F(ScopeStackTest, Set) {
-    ScopeStack<uint32_t> s;
+    ScopeStack<Symbol, uint32_t> s;
     Symbol a(1, ID());
     Symbol b(2, ID());
 
@@ -67,5 +67,25 @@
     EXPECT_EQ(s.Get(b), 25u);
 }
 
+TEST_F(ScopeStackTest, Clear) {
+    ScopeStack<Symbol, uint32_t> s;
+    Symbol a(1, ID());
+    Symbol b(2, ID());
+
+    EXPECT_EQ(s.Set(a, 5u), 0u);
+    EXPECT_EQ(s.Get(a), 5u);
+
+    s.Push();
+
+    EXPECT_EQ(s.Set(b, 10u), 0u);
+    EXPECT_EQ(s.Get(b), 10u);
+
+    s.Push();
+
+    s.Clear();
+    EXPECT_EQ(s.Get(a), 0u);
+    EXPECT_EQ(s.Get(b), 0u);
+}
+
 }  // namespace
 }  // namespace tint
diff --git a/src/tint/sem/abstract_float.cc b/src/tint/sem/abstract_float.cc
index 1f0c8b4..6f32e99 100644
--- a/src/tint/sem/abstract_float.cc
+++ b/src/tint/sem/abstract_float.cc
@@ -34,7 +34,7 @@
 }
 
 std::string AbstractFloat::FriendlyName(const SymbolTable&) const {
-    return "AbstractFloat";
+    return "abstract-float";
 }
 
 }  // namespace tint::sem
diff --git a/src/tint/sem/abstract_int.cc b/src/tint/sem/abstract_int.cc
index 6851514..682c50a 100644
--- a/src/tint/sem/abstract_int.cc
+++ b/src/tint/sem/abstract_int.cc
@@ -34,7 +34,7 @@
 }
 
 std::string AbstractInt::FriendlyName(const SymbolTable&) const {
-    return "AbstractInt";
+    return "abstract-int";
 }
 
 }  // namespace tint::sem
diff --git a/src/tint/sem/builtin.cc b/src/tint/sem/builtin.cc
index 0328a64..bb2878b 100644
--- a/src/tint/sem/builtin.cc
+++ b/src/tint/sem/builtin.cc
@@ -83,6 +83,10 @@
            i == sem::BuiltinType::kAtomicCompareExchangeWeak;
 }
 
+bool IsDP4aBuiltin(BuiltinType i) {
+    return i == sem::BuiltinType::kDot4I8Packed || i == sem::BuiltinType::kDot4U8Packed;
+}
+
 Builtin::Builtin(BuiltinType type,
                  const sem::Type* return_type,
                  std::vector<Parameter*> parameters,
@@ -135,6 +139,10 @@
     return IsAtomicBuiltin(type_);
 }
 
+bool Builtin::IsDP4a() const {
+    return IsDP4aBuiltin(type_);
+}
+
 bool Builtin::HasSideEffects() const {
     if (IsAtomic() && type_ != sem::BuiltinType::kAtomicLoad) {
         return true;
@@ -145,14 +153,11 @@
     return false;
 }
 
-ast::Enable::ExtensionKind Builtin::RequiredExtension() const {
-    switch (type_) {
-        case sem::BuiltinType::kDot4I8Packed:
-        case sem::BuiltinType::kDot4U8Packed:
-            return ast::Enable::ExtensionKind::kChromiumExperimentalDP4a;
-        default:
-            return ast::Enable::ExtensionKind::kNotAnExtension;
+ast::Extension Builtin::RequiredExtension() const {
+    if (IsDP4a()) {
+        return ast::Extension::kChromiumExperimentalDP4a;
     }
+    return ast::Extension::kNone;
 }
 
 }  // namespace tint::sem
diff --git a/src/tint/sem/builtin.h b/src/tint/sem/builtin.h
index 8d3e2bd..1dc61ad 100644
--- a/src/tint/sem/builtin.h
+++ b/src/tint/sem/builtin.h
@@ -18,6 +18,7 @@
 #include <string>
 #include <vector>
 
+#include "src/tint/ast/extension.h"
 #include "src/tint/sem/builtin_type.h"
 #include "src/tint/sem/call_target.h"
 #include "src/tint/sem/pipeline_stage_set.h"
@@ -70,6 +71,11 @@
 /// @returns true if the given `i` is a atomic builtin
 bool IsAtomicBuiltin(BuiltinType i);
 
+/// Determins if the given `i` is a DP4a builtin
+/// @param i the builtin
+/// @returns true if the given `i` is a DP4a builtin
+bool IsDP4aBuiltin(BuiltinType i);
+
 /// Builtin holds the semantic information for a builtin function.
 class Builtin final : public Castable<Builtin, CallTarget> {
   public:
@@ -130,13 +136,17 @@
     /// @returns true if builtin is a atomic builtin
     bool IsAtomic() const;
 
+    /// @returns true if builtin is a DP4a builtin (defined in the extension
+    /// chromium_experimental_DP4a)
+    bool IsDP4a() const;
+
     /// @returns true if intrinsic may have side-effects (i.e. writes to at least
     /// one of its inputs)
     bool HasSideEffects() const;
 
     /// @returns the required extension of this builtin function. Returns
-    /// ast::Enable::ExtensionKind::kNotAnExtension if no extension is required.
-    ast::Enable::ExtensionKind RequiredExtension() const;
+    /// ast::Extension::kNone if no extension is required.
+    ast::Extension RequiredExtension() const;
 
   private:
     const BuiltinType type_;
diff --git a/src/tint/sem/constant.cc b/src/tint/sem/constant.cc
index f5c82a2..98c724c 100644
--- a/src/tint/sem/constant.cc
+++ b/src/tint/sem/constant.cc
@@ -14,7 +14,6 @@
 
 #include "src/tint/sem/constant.h"
 
-#include <functional>
 #include <utility>
 
 #include "src/tint/debug.h"
@@ -25,24 +24,19 @@
 
 namespace {
 
-const Type* ElemType(const Type* ty, size_t num_elements) {
+const Type* CheckElemType(const Type* ty, size_t num_scalars) {
     diag::List diag;
-    if (ty->is_scalar()) {
-        if (num_elements != 1) {
-            TINT_ICE(Semantic, diag) << "sem::Constant() type <-> num_element mismatch. type: '"
-                                     << ty->TypeInfo().name << "' num_elements: " << num_elements;
+    if (ty->is_abstract_or_scalar() || ty->IsAnyOf<Vector, Matrix>()) {
+        uint32_t count = 0;
+        auto* el_ty = Type::ElementOf(ty, &count);
+        if (num_scalars != count) {
+            TINT_ICE(Semantic, diag) << "sem::Constant() type <-> scalar mismatch. type: '"
+                                     << ty->TypeInfo().name << "' scalar: " << num_scalars;
         }
-        return ty;
+        TINT_ASSERT(Semantic, el_ty->is_abstract_or_scalar());
+        return el_ty;
     }
-    if (auto* vec = ty->As<Vector>()) {
-        if (num_elements != vec->Width()) {
-            TINT_ICE(Semantic, diag) << "sem::Constant() type <-> num_element mismatch. type: '"
-                                     << ty->TypeInfo().name << "' num_elements: " << num_elements;
-        }
-        TINT_ASSERT(Semantic, vec->type()->is_scalar());
-        return vec->type();
-    }
-    TINT_UNREACHABLE(Semantic, diag) << "Unsupported sem::Constant type";
+    TINT_UNREACHABLE(Semantic, diag) << "Unsupported sem::Constant type: " << ty->TypeInfo().name;
     return nullptr;
 }
 
@@ -51,7 +45,7 @@
 Constant::Constant() {}
 
 Constant::Constant(const sem::Type* ty, Scalars els)
-    : type_(ty), elem_type_(ElemType(ty, els.size())), elems_(std::move(els)) {}
+    : type_(ty), elem_type_(CheckElemType(ty, els.size())), elems_(std::move(els)) {}
 
 Constant::Constant(const Constant&) = default;
 
@@ -60,16 +54,12 @@
 Constant& Constant::operator=(const Constant& rhs) = default;
 
 bool Constant::AnyZero() const {
-    for (size_t i = 0; i < Elements().size(); ++i) {
-        if (WithScalarAt(i, [&](auto&& s) {
-                // Use std::equal_to to work around -Wfloat-equal warnings
-                using T = std::remove_reference_t<decltype(s)>;
-                auto equal_to = std::equal_to<T>{};
-                if (equal_to(s, T(0))) {
-                    return true;
-                }
-                return false;
-            })) {
+    for (auto scalar : elems_) {
+        auto is_zero = [&](auto&& s) {
+            using T = std::remove_reference_t<decltype(s)>;
+            return s == T(0);
+        };
+        if (std::visit(is_zero, scalar)) {
             return true;
         }
     }
diff --git a/src/tint/sem/constant.h b/src/tint/sem/constant.h
index 673446f..ea143b6 100644
--- a/src/tint/sem/constant.h
+++ b/src/tint/sem/constant.h
@@ -15,6 +15,7 @@
 #ifndef SRC_TINT_SEM_CONSTANT_H_
 #define SRC_TINT_SEM_CONSTANT_H_
 
+#include <variant>
 #include <vector>
 
 #include "src/tint/program_builder.h"
@@ -26,34 +27,8 @@
 /// list of scalar values. Value may be of a scalar or vector type.
 class Constant {
   public:
-    /// Scalar holds a single constant scalar value, as a union of an i32, u32,
-    /// f32 or boolean.
-    union Scalar {
-        /// The scalar value as a i32
-        tint::i32 i32;
-        /// The scalar value as a u32
-        tint::u32 u32;
-        /// The scalar value as a f32
-        tint::f32 f32;
-        /// The scalar value as a bool
-        bool bool_;
-
-        /// Constructs the scalar with the i32 value `v`
-        /// @param v the value of the Scalar
-        Scalar(tint::i32 v) : i32(v) {}  // NOLINT
-
-        /// Constructs the scalar with the u32 value `v`
-        /// @param v the value of the Scalar
-        Scalar(tint::u32 v) : u32(v) {}  // NOLINT
-
-        /// Constructs the scalar with the f32 value `v`
-        /// @param v the value of the Scalar
-        Scalar(tint::f32 v) : f32(v) {}  // NOLINT
-
-        /// Constructs the scalar with the bool value `v`
-        /// @param v the value of the Scalar
-        Scalar(bool v) : bool_(v) {}  // NOLINT
-    };
+    /// Scalar holds a single constant scalar value - one of: AInt, AFloat or bool.
+    using Scalar = std::variant<AInt, AFloat, bool>;
 
     /// Scalars is a list of scalar values
     using Scalars = std::vector<Scalar>;
@@ -95,32 +70,25 @@
     /// @returns true if any scalar element is zero
     bool AnyZero() const;
 
-    /// Calls `func(s)` with s being the current scalar value at `index`.
-    /// `func` is typically a lambda of the form '[](auto&& s)'.
     /// @param index the index of the scalar value
-    /// @param func a function with signature `T(S)`
-    /// @return the value returned by func.
-    template <typename Func>
-    auto WithScalarAt(size_t index, Func&& func) const {
-        return Switch(
-            ElementType(),  //
-            [&](const I32*) { return func(elems_[index].i32); },
-            [&](const U32*) { return func(elems_[index].u32); },
-            [&](const F32*) { return func(elems_[index].f32); },
-            [&](const Bool*) { return func(elems_[index].bool_); },
-            [&](Default) {
-                diag::List diags;
-                TINT_UNREACHABLE(Semantic, diags)
-                    << "invalid scalar type " << type_->TypeInfo().name;
-                return func(u32(0u));
-            });
+    /// @return the value of the scalar at `index`, which must be of type `T`.
+    template <typename T>
+    T Element(size_t index) const {
+        return std::get<T>(elems_[index]);
     }
 
     /// @param index the index of the scalar value
     /// @return the value of the scalar `static_cast` to type T.
     template <typename T>
     T ElementAs(size_t index) const {
-        return WithScalarAt(index, [](auto val) { return static_cast<T>(val); });
+        return Cast<T>(elems_[index]);
+    }
+
+    /// @param s the input scalar
+    /// @returns the scalar `s` cast to the type `T`.
+    template <typename T>
+    static T Cast(Scalar s) {
+        return std::visit([](auto v) { return static_cast<T>(v); }, s);
     }
 
   private:
diff --git a/src/tint/sem/expression.cc b/src/tint/sem/expression.cc
index 40fbb58..57ec68b 100644
--- a/src/tint/sem/expression.cc
+++ b/src/tint/sem/expression.cc
@@ -16,6 +16,8 @@
 
 #include <utility>
 
+#include "src/tint/sem/materialize.h"
+
 TINT_INSTANTIATE_TYPEINFO(tint::sem::Expression);
 
 namespace tint::sem {
@@ -37,4 +39,11 @@
 
 Expression::~Expression() = default;
 
+const Expression* Expression::UnwrapMaterialize() const {
+    if (auto* m = As<Materialize>()) {
+        return m->Expr();
+    }
+    return this;
+}
+
 }  // namespace tint::sem
diff --git a/src/tint/sem/expression.h b/src/tint/sem/expression.h
index 13f493b..a783851 100644
--- a/src/tint/sem/expression.h
+++ b/src/tint/sem/expression.h
@@ -76,6 +76,9 @@
     /// @return true of this expression may have side effects
     bool HasSideEffects() const { return has_side_effects_; }
 
+    /// @return the inner expression node if this is a Materialize, otherwise this.
+    const Expression* UnwrapMaterialize() const;
+
   protected:
     /// The AST expression node for this semantic expression
     const ast::Expression* const declaration_;
diff --git a/src/tint/sem/expression_test.cc b/src/tint/sem/expression_test.cc
new file mode 100644
index 0000000..fc1adeb
--- /dev/null
+++ b/src/tint/sem/expression_test.cc
@@ -0,0 +1,39 @@
+// 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/expression.h"
+
+#include "src/tint/sem/test_helper.h"
+
+#include "src/tint/sem/materialize.h"
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::sem {
+namespace {
+
+using ExpressionTest = TestHelper;
+
+TEST_F(ExpressionTest, UnwrapMaterialize) {
+    auto* a = create<Expression>(/* declaration */ nullptr, create<I32>(), /* statement */ nullptr,
+                                 Constant{},
+                                 /* has_side_effects */ false, /* source_var */ nullptr);
+    auto* b = create<Materialize>(a, /* statement */ nullptr, Constant{create<I32>(), {1_a}});
+
+    EXPECT_EQ(a, a->UnwrapMaterialize());
+    EXPECT_EQ(a, b->UnwrapMaterialize());
+}
+
+}  // namespace
+}  // namespace tint::sem
diff --git a/src/tint/sem/f16.cc b/src/tint/sem/f16.cc
new file mode 100644
index 0000000..7da65fa
--- /dev/null
+++ b/src/tint/sem/f16.cc
@@ -0,0 +1,55 @@
+// 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/f16.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::sem::F16);
+
+namespace tint {
+namespace sem {
+
+F16::F16() = default;
+
+F16::F16(F16&&) = default;
+
+F16::~F16() = default;
+
+size_t F16::Hash() const {
+    return static_cast<size_t>(TypeInfo::Of<F16>().full_hashcode);
+}
+
+bool F16::Equals(const Type& other) const {
+    return other.Is<F16>();
+}
+
+std::string F16::FriendlyName(const SymbolTable&) const {
+    return "f16";
+}
+
+bool F16::IsConstructible() const {
+    return true;
+}
+
+uint32_t F16::Size() const {
+    return 2;
+}
+
+uint32_t F16::Align() const {
+    return 2;
+}
+
+}  // namespace sem
+}  // namespace tint
diff --git a/src/tint/sem/f16.h b/src/tint/sem/f16.h
new file mode 100644
index 0000000..72984c1
--- /dev/null
+++ b/src/tint/sem/f16.h
@@ -0,0 +1,58 @@
+// 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_F16_H_
+#define SRC_TINT_SEM_F16_H_
+
+#include <string>
+
+#include "src/tint/sem/type.h"
+
+namespace tint::sem {
+
+/// A float 16 type
+class F16 : public Castable<F16, Type> {
+  public:
+    /// Constructor
+    F16();
+    /// Move constructor
+    F16(F16&&);
+    ~F16() override;
+
+    /// @returns a hash of the type.
+    size_t Hash() const override;
+
+    /// @param other the other type to compare against
+    /// @returns true if the this type is equal to the given type
+    bool Equals(const Type& other) const override;
+
+    /// @param symbols the program's symbol table
+    /// @returns the name for this type that closely resembles how it would be
+    /// declared in WGSL.
+    std::string FriendlyName(const SymbolTable& symbols) const override;
+
+    /// @returns true if constructible as per
+    /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+    bool IsConstructible() const override;
+
+    /// @returns the size in bytes of the type.
+    uint32_t Size() const override;
+
+    /// @returns the alignment in bytes of the type.
+    uint32_t Align() const override;
+};
+
+}  // namespace tint::sem
+
+#endif  // SRC_TINT_SEM_F16_H_
diff --git a/src/tint/sem/f16_test.cc b/src/tint/sem/f16_test.cc
new file mode 100644
index 0000000..28fd0da
--- /dev/null
+++ b/src/tint/sem/f16_test.cc
@@ -0,0 +1,48 @@
+// 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/test_helper.h"
+#include "src/tint/sem/texture.h"
+
+namespace tint::sem {
+namespace {
+
+using F16Test = TestHelper;
+
+TEST_F(F16Test, Creation) {
+    auto* a = create<F16>();
+    auto* b = create<F16>();
+    EXPECT_EQ(a, b);
+}
+
+TEST_F(F16Test, Hash) {
+    auto* a = create<F16>();
+    auto* b = create<F16>();
+    EXPECT_EQ(a->Hash(), b->Hash());
+}
+
+TEST_F(F16Test, Equals) {
+    auto* a = create<F16>();
+    auto* b = create<F16>();
+    EXPECT_TRUE(a->Equals(*b));
+    EXPECT_FALSE(a->Equals(Void{}));
+}
+
+TEST_F(F16Test, FriendlyName) {
+    F16 f;
+    EXPECT_EQ(f.FriendlyName(Symbols()), "f16");
+}
+
+}  // namespace
+}  // namespace tint::sem
diff --git a/src/tint/sem/materialize.cc b/src/tint/sem/materialize.cc
new file mode 100644
index 0000000..76dd9d4
--- /dev/null
+++ b/src/tint/sem/materialize.cc
@@ -0,0 +1,36 @@
+// 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/materialize.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::sem::Materialize);
+
+namespace tint::sem {
+
+Materialize::Materialize(const Expression* expr, const Statement* statement, Constant constant)
+    : Base(/* declaration */ expr->Declaration(),
+           /* 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());
+}
+
+Materialize::~Materialize() = default;
+
+}  // namespace tint::sem
diff --git a/src/tint/sem/materialize.h b/src/tint/sem/materialize.h
new file mode 100644
index 0000000..a7c0e3a
--- /dev/null
+++ b/src/tint/sem/materialize.h
@@ -0,0 +1,48 @@
+// 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_MATERIALIZE_H_
+#define SRC_TINT_SEM_MATERIALIZE_H_
+
+#include "src/tint/sem/expression.h"
+
+namespace tint::sem {
+
+/// Materialize is a semantic expression which represents the materialization of a value of an
+/// abstract numeric type to a value of a concrete type.
+/// Abstract numeric materialization is implicit in WGSL, so the Materialize semantic node shares
+/// the same AST node as the inner semantic node.
+/// Abstract numerics types may only be used by compile-time expressions, so a Materialize semantic
+/// node must have a valid Constant value.
+class Materialize final : public Castable<Materialize, Expression> {
+  public:
+    /// Constructor
+    /// @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);
+
+    /// Destructor
+    ~Materialize() override;
+
+    /// @return the target of the call
+    const Expression* Expr() const { return expr_; }
+
+  private:
+    Expression const* const expr_;
+};
+
+}  // namespace tint::sem
+
+#endif  // SRC_TINT_SEM_MATERIALIZE_H_
diff --git a/src/tint/sem/module.cc b/src/tint/sem/module.cc
index 83b7136..7c60650 100644
--- a/src/tint/sem/module.cc
+++ b/src/tint/sem/module.cc
@@ -21,8 +21,8 @@
 
 namespace tint::sem {
 
-Module::Module(std::vector<const ast::Node*> dep_ordered_decls)
-    : dep_ordered_decls_(std::move(dep_ordered_decls)) {}
+Module::Module(std::vector<const ast::Node*> dep_ordered_decls, ast::Extensions extensions)
+    : dep_ordered_decls_(std::move(dep_ordered_decls)), extensions_(std::move(extensions)) {}
 
 Module::~Module() = default;
 
diff --git a/src/tint/sem/module.h b/src/tint/sem/module.h
index c265d4e..a7b3d45 100644
--- a/src/tint/sem/module.h
+++ b/src/tint/sem/module.h
@@ -17,6 +17,7 @@
 
 #include <vector>
 
+#include "src/tint/ast/extension.h"
 #include "src/tint/sem/node.h"
 
 // Forward declarations
@@ -33,7 +34,8 @@
   public:
     /// Constructor
     /// @param dep_ordered_decls the dependency-ordered module-scope declarations
-    explicit Module(std::vector<const ast::Node*> dep_ordered_decls);
+    /// @param extensions the list of enabled extensions in the module
+    Module(std::vector<const ast::Node*> dep_ordered_decls, ast::Extensions extensions);
 
     /// Destructor
     ~Module() override;
@@ -43,8 +45,12 @@
         return dep_ordered_decls_;
     }
 
+    /// @returns the list of enabled extensions in the module
+    const ast::Extensions& Extensions() const { return extensions_; }
+
   private:
     const std::vector<const ast::Node*> dep_ordered_decls_;
+    ast::Extensions extensions_;
 };
 
 }  // namespace tint::sem
diff --git a/src/tint/sem/parameter_usage.cc b/src/tint/sem/parameter_usage.cc
index 4891bba..010272a 100644
--- a/src/tint/sem/parameter_usage.cc
+++ b/src/tint/sem/parameter_usage.cc
@@ -56,6 +56,24 @@
             return "texture";
         case ParameterUsage::kValue:
             return "value";
+        case ParameterUsage::kW:
+            return "w";
+        case ParameterUsage::kX:
+            return "x";
+        case ParameterUsage::kXy:
+            return "xy";
+        case ParameterUsage::kXyz:
+            return "xyz";
+        case ParameterUsage::kY:
+            return "y";
+        case ParameterUsage::kYz:
+            return "yz";
+        case ParameterUsage::kZ:
+            return "z";
+        case ParameterUsage::kZw:
+            return "zw";
+        case ParameterUsage::kZyw:
+            return "zyw";
     }
     return "<unknown>";
 }
diff --git a/src/tint/sem/parameter_usage.h b/src/tint/sem/parameter_usage.h
index 85ef64f..b17ae3e 100644
--- a/src/tint/sem/parameter_usage.h
+++ b/src/tint/sem/parameter_usage.h
@@ -44,6 +44,15 @@
     kSampler,
     kTexture,
     kValue,
+    kW,
+    kX,
+    kXy,
+    kXyz,
+    kY,
+    kYz,
+    kZ,
+    kZw,
+    kZyw,
 };
 
 /// @returns a string representation of the given parameter usage.
diff --git a/src/tint/sem/switch_statement.h b/src/tint/sem/switch_statement.h
index a5ef659..a6b5c00 100644
--- a/src/tint/sem/switch_statement.h
+++ b/src/tint/sem/switch_statement.h
@@ -15,6 +15,8 @@
 #ifndef SRC_TINT_SEM_SWITCH_STATEMENT_H_
 #define SRC_TINT_SEM_SWITCH_STATEMENT_H_
 
+#include <vector>
+
 #include "src/tint/sem/block_statement.h"
 
 // Forward declarations
@@ -22,6 +24,10 @@
 class CaseStatement;
 class SwitchStatement;
 }  // namespace tint::ast
+namespace tint::sem {
+class CaseStatement;
+class Expression;
+}  // namespace tint::sem
 
 namespace tint::sem {
 
@@ -41,6 +47,15 @@
 
     /// @return the AST node for this statement
     const ast::SwitchStatement* Declaration() const;
+
+    /// @returns the case statements for this switch
+    std::vector<const CaseStatement*>& Cases() { return cases_; }
+
+    /// @returns the case statements for this switch
+    const std::vector<const CaseStatement*>& Cases() const { return cases_; }
+
+  private:
+    std::vector<const CaseStatement*> cases_;
 };
 
 /// Holds semantic information about a switch case statement
@@ -66,8 +81,15 @@
     /// @returns the case body block statement
     const BlockStatement* Body() const { return body_; }
 
+    /// @returns the selectors for the case
+    std::vector<const Expression*>& Selectors() { return selectors_; }
+
+    /// @returns the selectors for the case
+    const std::vector<const Expression*>& Selectors() const { return selectors_; }
+
   private:
     const BlockStatement* body_ = nullptr;
+    std::vector<const Expression*> selectors_;
 };
 
 }  // namespace tint::sem
diff --git a/src/tint/sem/test_helper.h b/src/tint/sem/test_helper.h
index 94f66e4..e1b4eb3 100644
--- a/src/tint/sem/test_helper.h
+++ b/src/tint/sem/test_helper.h
@@ -44,4 +44,16 @@
 
 }  // namespace tint::sem
 
+/// Helper macro for testing that a semantic type was as expected
+#define EXPECT_TYPE(GOT, EXPECT)                                         \
+    do {                                                                 \
+        const sem::Type* got = GOT;                                      \
+        const sem::Type* expect = EXPECT;                                \
+        if (got != expect) {                                             \
+            ADD_FAILURE() << #GOT " != " #EXPECT "\n"                    \
+                          << "  " #GOT ": " << FriendlyName(got) << "\n" \
+                          << "  " #EXPECT ": " << FriendlyName(expect);  \
+        }                                                                \
+    } while (false)
+
 #endif  // SRC_TINT_SEM_TEST_HELPER_H_
diff --git a/src/tint/sem/type.cc b/src/tint/sem/type.cc
index 5776ff4..40666e3 100644
--- a/src/tint/sem/type.cc
+++ b/src/tint/sem/type.cc
@@ -14,7 +14,11 @@
 
 #include "src/tint/sem/type.h"
 
+#include "src/tint/sem/abstract_float.h"
+#include "src/tint/sem/abstract_int.h"
+#include "src/tint/sem/array.h"
 #include "src/tint/sem/bool.h"
+#include "src/tint/sem/f16.h"
 #include "src/tint/sem/f32.h"
 #include "src/tint/sem/i32.h"
 #include "src/tint/sem/matrix.h"
@@ -64,15 +68,19 @@
 }
 
 bool Type::is_scalar() const {
-    return IsAnyOf<F32, U32, I32, Bool>();
+    return IsAnyOf<F16, F32, U32, I32, Bool>();
+}
+
+bool Type::is_abstract_or_scalar() const {
+    return IsAnyOf<F16, F32, U32, I32, Bool, AbstractNumeric>();
 }
 
 bool Type::is_numeric_scalar() const {
-    return IsAnyOf<F32, U32, I32>();
+    return IsAnyOf<F16, F32, U32, I32>();
 }
 
 bool Type::is_float_scalar() const {
-    return Is<F32>();
+    return IsAnyOf<F16, F32>();
 }
 
 bool Type::is_float_matrix() const {
@@ -152,4 +160,98 @@
     return IsAnyOf<Sampler, Texture>();
 }
 
+uint32_t Type::ConversionRank(const Type* from, const Type* to) {
+    if (from->UnwrapRef() == to) {
+        return 0;
+    }
+    return Switch(
+        from,
+        [&](const AbstractFloat*) {
+            return Switch(
+                to,                             //
+                [&](const F32*) { return 1; },  //
+                [&](const F16*) { return 2; },  //
+                [&](Default) { return kNoConversion; });
+        },
+        [&](const AbstractInt*) {
+            return Switch(
+                to,                                       //
+                [&](const I32*) { return 3; },            //
+                [&](const U32*) { return 4; },            //
+                [&](const AbstractFloat*) { return 5; },  //
+                [&](const F32*) { return 6; },            //
+                [&](const F16*) { return 7; },            //
+                [&](Default) { return kNoConversion; });
+        },
+        [&](const Vector* from_vec) {
+            if (auto* to_vec = to->As<Vector>()) {
+                if (from_vec->Width() == to_vec->Width()) {
+                    return ConversionRank(from_vec->type(), to_vec->type());
+                }
+            }
+            return kNoConversion;
+        },
+        [&](const Matrix* from_mat) {
+            if (auto* to_mat = to->As<Matrix>()) {
+                if (from_mat->columns() == to_mat->columns() &&
+                    from_mat->rows() == to_mat->rows()) {
+                    return ConversionRank(from_mat->type(), to_mat->type());
+                }
+            }
+            return kNoConversion;
+        },
+        [&](Default) { return kNoConversion; });
+}
+
+const Type* Type::ElementOf(const Type* ty, uint32_t* count /* = nullptr */) {
+    if (ty->is_abstract_or_scalar()) {
+        if (count) {
+            *count = 1;
+        }
+        return ty;
+    }
+    return Switch(
+        ty,  //
+        [&](const Vector* v) {
+            if (count) {
+                *count = v->Width();
+            }
+            return v->type();
+        },
+        [&](const Matrix* m) {
+            if (count) {
+                *count = m->columns() * m->rows();
+            }
+            return m->type();
+        },
+        [&](const Array* a) {
+            if (count) {
+                *count = a->Count();
+            }
+            return a->ElemType();
+        });
+}
+
+const sem::Type* Type::Common(Type const* const* types, size_t count) {
+    if (count == 0) {
+        return nullptr;
+    }
+    const auto* common = types[0];
+    for (size_t i = 1; i < count; i++) {
+        auto* ty = types[i];
+        if (ty == common) {
+            continue;  // ty == common
+        }
+        if (sem::Type::ConversionRank(ty, common) != sem::Type::kNoConversion) {
+            continue;  // ty can be converted to common.
+        }
+        if (sem::Type::ConversionRank(common, ty) != sem::Type::kNoConversion) {
+            common = ty;  // common can be converted to ty.
+            continue;
+        }
+        return nullptr;  // Conversion is not valid.
+    }
+    return common;
+}
+
 }  // namespace tint::sem
diff --git a/src/tint/sem/type.h b/src/tint/sem/type.h
index 271e1d7..9987637 100644
--- a/src/tint/sem/type.h
+++ b/src/tint/sem/type.h
@@ -71,6 +71,8 @@
 
     /// @returns true if this type is a scalar
     bool is_scalar() const;
+    /// @returns true if this type is a scalar or an abstract numeric
+    bool is_abstract_or_scalar() const;
     /// @returns true if this type is a numeric scalar
     bool is_numeric_scalar() const;
     /// @returns true if this type is a float scalar
@@ -114,6 +116,40 @@
     /// @returns true if this type is a handle type
     bool is_handle() const;
 
+    /// kNoConversion is returned from ConversionRank() when the implicit conversion is not
+    /// permitted.
+    static constexpr uint32_t kNoConversion = 0xffffffffu;
+
+    /// ConversionRank returns the implicit conversion rank when attempting to convert `from` to
+    /// `to`. Lower ranks are preferred over higher ranks.
+    /// @param from the source type
+    /// @param to the destination type
+    /// @returns the rank value for converting from type `from` to type `to`, or #kNoConversion if
+    /// the implicit conversion is not allowed.
+    /// @see https://www.w3.org/TR/WGSL/#conversion-rank
+    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.
+    static const Type* ElementOf(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,
+    /// or nullptr if there is no consistent common type across all types in `types`.
+    /// @see https://www.w3.org/TR/WGSL/#conversion-rank
+    static const sem::Type* Common(Type const* const* types, size_t count);
+
+    /// @param types an initializer_list of `const Type*`.
+    /// @returns the lowest-ranking type that all types in `types` can be implicitly converted to,
+    /// or nullptr if there is no consistent common type across all types in `types`.
+    /// @see https://www.w3.org/TR/WGSL/#conversion-rank
+    static const sem::Type* Common(std::initializer_list<const Type*> types) {
+        return Common(types.begin(), types.size());
+    }
+
   protected:
     Type();
 };
diff --git a/src/tint/sem/type_mappings.h b/src/tint/sem/type_mappings.h
index 8425f14..0e54eed 100644
--- a/src/tint/sem/type_mappings.h
+++ b/src/tint/sem/type_mappings.h
@@ -30,6 +30,7 @@
 class Statement;
 class Struct;
 class StructMember;
+class SwitchStatement;
 class Type;
 class TypeDecl;
 class Variable;
@@ -46,6 +47,7 @@
 class Statement;
 class Struct;
 class StructMember;
+class SwitchStatement;
 class Type;
 class Variable;
 }  // namespace tint::sem
@@ -59,7 +61,6 @@
 struct TypeMappings {
     //! @cond Doxygen_Suppress
     Array* operator()(ast::Array*);
-    Call* operator()(ast::CallExpression*);
     Expression* operator()(ast::Expression*);
     ForLoopStatement* operator()(ast::ForLoopStatement*);
     Function* operator()(ast::Function*);
@@ -69,6 +70,7 @@
     Statement* operator()(ast::Statement*);
     Struct* operator()(ast::Struct*);
     StructMember* operator()(ast::StructMember*);
+    SwitchStatement* operator()(ast::SwitchStatement*);
     Type* operator()(ast::Type*);
     Type* operator()(ast::TypeDecl*);
     Variable* operator()(ast::Variable*);
diff --git a/src/tint/sem/type_test.cc b/src/tint/sem/type_test.cc
new file mode 100644
index 0000000..c11efea
--- /dev/null
+++ b/src/tint/sem/type_test.cc
@@ -0,0 +1,393 @@
+// 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/abstract_float.h"
+#include "src/tint/sem/abstract_int.h"
+#include "src/tint/sem/reference.h"
+#include "src/tint/sem/test_helper.h"
+
+namespace tint::sem {
+namespace {
+
+using TypeTest = TestHelper;
+
+TEST_F(TypeTest, ConversionRank) {
+    auto* af = create<AbstractFloat>();
+    auto* ai = create<AbstractInt>();
+    auto* f32 = create<F32>();
+    auto* f16 = create<F16>();
+    auto* i32 = create<I32>();
+    auto* u32 = create<U32>();
+    auto* vec3_f32 = create<Vector>(f32, 3u);
+    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* vec3_af = create<Vector>(af, 3u);
+    auto* vec3_ai = create<Vector>(ai, 3u);
+    auto* mat3x4_f32 = create<Matrix>(vec4_f32, 3u);
+    auto* mat4x3_f32 = create<Matrix>(vec3_f32, 4u);
+    auto* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
+    auto* mat4x3_af = create<Matrix>(vec3_af, 4u);
+    auto* ref_u32 = create<Reference>(u32, ast::StorageClass::kPrivate, ast::Access::kReadWrite);
+
+    EXPECT_EQ(Type::ConversionRank(i32, i32), 0u);
+    EXPECT_EQ(Type::ConversionRank(f32, f32), 0u);
+    EXPECT_EQ(Type::ConversionRank(u32, u32), 0u);
+    EXPECT_EQ(Type::ConversionRank(vec3_f32, vec3_f32), 0u);
+    EXPECT_EQ(Type::ConversionRank(vec3_f16, vec3_f16), 0u);
+    EXPECT_EQ(Type::ConversionRank(vec4_f32, vec4_f32), 0u);
+    EXPECT_EQ(Type::ConversionRank(vec3_u32, vec3_u32), 0u);
+    EXPECT_EQ(Type::ConversionRank(vec3_i32, vec3_i32), 0u);
+    EXPECT_EQ(Type::ConversionRank(vec3_af, vec3_af), 0u);
+    EXPECT_EQ(Type::ConversionRank(vec3_ai, vec3_ai), 0u);
+    EXPECT_EQ(Type::ConversionRank(mat3x4_f32, mat3x4_f32), 0u);
+    EXPECT_EQ(Type::ConversionRank(mat4x3_f32, mat4x3_f32), 0u);
+    EXPECT_EQ(Type::ConversionRank(mat4x3_f16, mat4x3_f16), 0u);
+    EXPECT_EQ(Type::ConversionRank(mat4x3_af, mat4x3_af), 0u);
+    EXPECT_EQ(Type::ConversionRank(ref_u32, u32), 0u);
+
+    EXPECT_EQ(Type::ConversionRank(af, f32), 1u);
+    EXPECT_EQ(Type::ConversionRank(vec3_af, vec3_f32), 1u);
+    EXPECT_EQ(Type::ConversionRank(mat4x3_af, mat4x3_f32), 1u);
+    EXPECT_EQ(Type::ConversionRank(af, f16), 2u);
+    EXPECT_EQ(Type::ConversionRank(vec3_af, vec3_f16), 2u);
+    EXPECT_EQ(Type::ConversionRank(mat4x3_af, mat4x3_f16), 2u);
+    EXPECT_EQ(Type::ConversionRank(ai, i32), 3u);
+    EXPECT_EQ(Type::ConversionRank(vec3_ai, vec3_i32), 3u);
+    EXPECT_EQ(Type::ConversionRank(ai, u32), 4u);
+    EXPECT_EQ(Type::ConversionRank(vec3_ai, vec3_u32), 4u);
+    EXPECT_EQ(Type::ConversionRank(ai, af), 5u);
+    EXPECT_EQ(Type::ConversionRank(ai, f32), 6u);
+    EXPECT_EQ(Type::ConversionRank(ai, f16), 7u);
+
+    EXPECT_EQ(Type::ConversionRank(i32, f32), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(f32, u32), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(u32, i32), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(vec3_u32, vec3_f32), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(vec3_f32, vec4_f32), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(mat3x4_f32, mat4x3_f32), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(mat4x3_f32, mat3x4_f32), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(mat4x3_f32, mat4x3_af), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(f32, af), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(f16, af), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(vec3_f16, vec3_af), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(mat4x3_f16, mat4x3_af), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(i32, af), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(u32, af), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(af, ai), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(f32, ai), Type::kNoConversion);
+    EXPECT_EQ(Type::ConversionRank(f16, ai), Type::kNoConversion);
+}
+
+TEST_F(TypeTest, ElementOf) {
+    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* arr_i32 = create<Array>(
+        /* element */ i32,
+        /* count */ 5u,
+        /* align */ 4u,
+        /* size */ 5u * 4u,
+        /* stride */ 5u * 4u,
+        /* implicit_stride */ 5u * 4u);
+
+    // No count
+    EXPECT_TYPE(Type::ElementOf(f32), f32);
+    EXPECT_TYPE(Type::ElementOf(f16), f16);
+    EXPECT_TYPE(Type::ElementOf(i32), i32);
+    EXPECT_TYPE(Type::ElementOf(u32), u32);
+    EXPECT_TYPE(Type::ElementOf(vec2_f32), f32);
+    EXPECT_TYPE(Type::ElementOf(vec3_f16), f16);
+    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(arr_i32), i32);
+
+    // With count
+    uint32_t count = 0;
+    EXPECT_TYPE(Type::ElementOf(f32, &count), f32);
+    EXPECT_EQ(count, 1u);
+    count = 0;
+    EXPECT_TYPE(Type::ElementOf(f16, &count), f16);
+    EXPECT_EQ(count, 1u);
+    count = 0;
+    EXPECT_TYPE(Type::ElementOf(i32, &count), i32);
+    EXPECT_EQ(count, 1u);
+    count = 0;
+    EXPECT_TYPE(Type::ElementOf(u32, &count), u32);
+    EXPECT_EQ(count, 1u);
+    count = 0;
+    EXPECT_TYPE(Type::ElementOf(vec2_f32, &count), f32);
+    EXPECT_EQ(count, 2u);
+    count = 0;
+    EXPECT_TYPE(Type::ElementOf(vec3_f16, &count), f16);
+    EXPECT_EQ(count, 3u);
+    count = 0;
+    EXPECT_TYPE(Type::ElementOf(vec4_f32, &count), f32);
+    EXPECT_EQ(count, 4u);
+    count = 0;
+    EXPECT_TYPE(Type::ElementOf(vec3_u32, &count), u32);
+    EXPECT_EQ(count, 3u);
+    count = 0;
+    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;
+    EXPECT_TYPE(Type::ElementOf(arr_i32, &count), i32);
+    EXPECT_EQ(count, 5u);
+}
+
+TEST_F(TypeTest, Common2) {
+    auto* ai = create<AbstractInt>();
+    auto* af = create<AbstractFloat>();
+    auto* f32 = create<F32>();
+    auto* f16 = create<F16>();
+    auto* i32 = create<I32>();
+    auto* u32 = create<U32>();
+
+    EXPECT_TYPE(Type::Common({ai, ai}), ai);
+    EXPECT_TYPE(Type::Common({af, af}), af);
+    EXPECT_TYPE(Type::Common({f32, f32}), f32);
+    EXPECT_TYPE(Type::Common({f16, f16}), f16);
+    EXPECT_TYPE(Type::Common({i32, i32}), i32);
+    EXPECT_TYPE(Type::Common({u32, u32}), u32);
+
+    EXPECT_TYPE(Type::Common({i32, u32}), nullptr);
+    EXPECT_TYPE(Type::Common({u32, f32}), nullptr);
+    EXPECT_TYPE(Type::Common({f32, f16}), nullptr);
+    EXPECT_TYPE(Type::Common({f16, i32}), nullptr);
+
+    EXPECT_TYPE(Type::Common({ai, af}), af);
+    EXPECT_TYPE(Type::Common({ai, f32}), f32);
+    EXPECT_TYPE(Type::Common({ai, f16}), f16);
+    EXPECT_TYPE(Type::Common({ai, i32}), i32);
+    EXPECT_TYPE(Type::Common({ai, u32}), u32);
+
+    EXPECT_TYPE(Type::Common({af, ai}), af);
+    EXPECT_TYPE(Type::Common({f32, ai}), f32);
+    EXPECT_TYPE(Type::Common({f16, ai}), f16);
+    EXPECT_TYPE(Type::Common({i32, ai}), i32);
+    EXPECT_TYPE(Type::Common({u32, ai}), u32);
+
+    EXPECT_TYPE(Type::Common({ai, af}), af);
+    EXPECT_TYPE(Type::Common({f32, af}), f32);
+    EXPECT_TYPE(Type::Common({f16, af}), f16);
+    EXPECT_TYPE(Type::Common({i32, af}), nullptr);
+    EXPECT_TYPE(Type::Common({u32, af}), nullptr);
+
+    EXPECT_TYPE(Type::Common({af, ai}), af);
+    EXPECT_TYPE(Type::Common({af, f32}), f32);
+    EXPECT_TYPE(Type::Common({af, f16}), f16);
+    EXPECT_TYPE(Type::Common({af, i32}), nullptr);
+    EXPECT_TYPE(Type::Common({af, u32}), nullptr);
+
+    auto* vec3_ai = create<Vector>(ai, 3u);
+    auto* vec3_af = create<Vector>(af, 3u);
+    auto* vec3_f32 = create<Vector>(f32, 3u);
+    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);
+
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_ai}), vec3_ai);
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_af}), vec3_af);
+    EXPECT_TYPE(Type::Common({vec3_f32, vec3_f32}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_f16, vec3_f16}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec4_f32, vec4_f32}), vec4_f32);
+    EXPECT_TYPE(Type::Common({vec3_u32, vec3_u32}), vec3_u32);
+    EXPECT_TYPE(Type::Common({vec3_i32, vec3_i32}), vec3_i32);
+
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_f32}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_f16}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec4_f32}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_u32}), vec3_u32);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_i32}), vec3_i32);
+
+    EXPECT_TYPE(Type::Common({vec3_f32, vec3_ai}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_f16, vec3_ai}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec4_f32, vec3_ai}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_u32, vec3_ai}), vec3_u32);
+    EXPECT_TYPE(Type::Common({vec3_i32, vec3_ai}), vec3_i32);
+
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_f32}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_f16}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec3_af, vec4_f32}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_u32}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_i32}), nullptr);
+
+    EXPECT_TYPE(Type::Common({vec3_f32, vec3_af}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_f16, vec3_af}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec4_f32, vec3_af}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_u32, vec3_af}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_i32, vec3_af}), nullptr);
+
+    auto* mat4x3_af = create<Matrix>(vec3_af, 4u);
+    auto* mat3x4_f32 = create<Matrix>(vec4_f32, 3u);
+    auto* mat4x3_f32 = create<Matrix>(vec3_f32, 4u);
+    auto* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
+
+    EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_af}), mat4x3_af);
+    EXPECT_TYPE(Type::Common({mat3x4_f32, mat3x4_f32}), mat3x4_f32);
+    EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_f32}), mat4x3_f32);
+    EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_f16}), mat4x3_f16);
+
+    EXPECT_TYPE(Type::Common({mat4x3_af, mat3x4_f32}), nullptr);
+    EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f32}), mat4x3_f32);
+    EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f16}), mat4x3_f16);
+
+    EXPECT_TYPE(Type::Common({mat3x4_f32, mat4x3_af}), nullptr);
+    EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_af}), mat4x3_f32);
+    EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_af}), mat4x3_f16);
+}
+
+TEST_F(TypeTest, Common3) {
+    auto* ai = create<AbstractInt>();
+    auto* af = create<AbstractFloat>();
+    auto* f32 = create<F32>();
+    auto* f16 = create<F16>();
+    auto* i32 = create<I32>();
+    auto* u32 = create<U32>();
+
+    EXPECT_TYPE(Type::Common({ai, ai, ai}), ai);
+    EXPECT_TYPE(Type::Common({af, af, af}), af);
+    EXPECT_TYPE(Type::Common({f32, f32, f32}), f32);
+    EXPECT_TYPE(Type::Common({f16, f16, f16}), f16);
+    EXPECT_TYPE(Type::Common({i32, i32, i32}), i32);
+    EXPECT_TYPE(Type::Common({u32, u32, u32}), u32);
+
+    EXPECT_TYPE(Type::Common({ai, af, ai}), af);
+    EXPECT_TYPE(Type::Common({ai, f32, ai}), f32);
+    EXPECT_TYPE(Type::Common({ai, f16, ai}), f16);
+    EXPECT_TYPE(Type::Common({ai, i32, ai}), i32);
+    EXPECT_TYPE(Type::Common({ai, u32, ai}), u32);
+
+    EXPECT_TYPE(Type::Common({af, ai, af}), af);
+    EXPECT_TYPE(Type::Common({f32, ai, f32}), f32);
+    EXPECT_TYPE(Type::Common({f16, ai, f16}), f16);
+    EXPECT_TYPE(Type::Common({i32, ai, i32}), i32);
+    EXPECT_TYPE(Type::Common({u32, ai, u32}), u32);
+
+    EXPECT_TYPE(Type::Common({ai, f32, ai}), f32);
+    EXPECT_TYPE(Type::Common({ai, f16, ai}), f16);
+    EXPECT_TYPE(Type::Common({ai, i32, ai}), i32);
+    EXPECT_TYPE(Type::Common({ai, u32, ai}), u32);
+
+    EXPECT_TYPE(Type::Common({f32, ai, f32}), f32);
+    EXPECT_TYPE(Type::Common({f16, ai, f16}), f16);
+    EXPECT_TYPE(Type::Common({i32, ai, i32}), i32);
+    EXPECT_TYPE(Type::Common({u32, ai, u32}), u32);
+
+    EXPECT_TYPE(Type::Common({af, f32, af}), f32);
+    EXPECT_TYPE(Type::Common({af, f16, af}), f16);
+    EXPECT_TYPE(Type::Common({af, i32, af}), nullptr);
+    EXPECT_TYPE(Type::Common({af, u32, af}), nullptr);
+
+    EXPECT_TYPE(Type::Common({f32, af, f32}), f32);
+    EXPECT_TYPE(Type::Common({f16, af, f16}), f16);
+    EXPECT_TYPE(Type::Common({i32, af, i32}), nullptr);
+    EXPECT_TYPE(Type::Common({u32, af, u32}), nullptr);
+
+    EXPECT_TYPE(Type::Common({ai, af, f32}), f32);
+    EXPECT_TYPE(Type::Common({ai, af, f16}), f16);
+    EXPECT_TYPE(Type::Common({ai, af, i32}), nullptr);
+    EXPECT_TYPE(Type::Common({ai, af, u32}), nullptr);
+
+    auto* vec3_ai = create<Vector>(ai, 3u);
+    auto* vec3_af = create<Vector>(af, 3u);
+    auto* vec3_f32 = create<Vector>(f32, 3u);
+    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);
+
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_ai, vec3_ai}), vec3_ai);
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_af, vec3_af}), vec3_af);
+    EXPECT_TYPE(Type::Common({vec3_f32, vec3_f32, vec3_f32}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_f16, vec3_f16, vec3_f16}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec4_f32, vec4_f32, vec4_f32}), vec4_f32);
+    EXPECT_TYPE(Type::Common({vec3_u32, vec3_u32, vec3_u32}), vec3_u32);
+    EXPECT_TYPE(Type::Common({vec3_i32, vec3_i32, vec3_i32}), vec3_i32);
+
+    EXPECT_TYPE(Type::Common({vec3_f32, vec3_ai, vec3_f32}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_f16, vec3_ai, vec3_f16}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec4_f32, vec3_ai, vec4_f32}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_u32, vec3_ai, vec3_u32}), vec3_u32);
+    EXPECT_TYPE(Type::Common({vec3_i32, vec3_ai, vec3_i32}), vec3_i32);
+
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_f32, vec3_ai}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_f16, vec3_ai}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec4_f32, vec3_ai}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_u32, vec3_ai}), vec3_u32);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_i32, vec3_ai}), vec3_i32);
+
+    EXPECT_TYPE(Type::Common({vec3_f32, vec3_af, vec3_f32}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_f16, vec3_af, vec3_f16}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec4_f32, vec3_af, vec4_f32}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_u32, vec3_af, vec3_u32}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_i32, vec3_af, vec3_i32}), nullptr);
+
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_f32, vec3_af}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_f16, vec3_af}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec3_af, vec4_f32, vec3_af}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_u32, vec3_af}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_af, vec3_i32, vec3_af}), nullptr);
+
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_f32}), vec3_f32);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_f16}), vec3_f16);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec4_f32}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_u32}), nullptr);
+    EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_i32}), nullptr);
+
+    auto* mat4x3_af = create<Matrix>(vec3_af, 4u);
+    auto* mat3x4_f32 = create<Matrix>(vec4_f32, 3u);
+    auto* mat4x3_f32 = create<Matrix>(vec3_f32, 4u);
+    auto* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
+
+    EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_af, mat4x3_af}), mat4x3_af);
+    EXPECT_TYPE(Type::Common({mat3x4_f32, mat3x4_f32, mat3x4_f32}), mat3x4_f32);
+    EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_f32, mat4x3_f32}), mat4x3_f32);
+    EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_f16, mat4x3_f16}), mat4x3_f16);
+
+    EXPECT_TYPE(Type::Common({mat3x4_f32, mat4x3_af, mat3x4_f32}), nullptr);
+    EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_af, mat4x3_f32}), mat4x3_f32);
+    EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_af, mat4x3_f16}), mat4x3_f16);
+
+    EXPECT_TYPE(Type::Common({mat4x3_af, mat3x4_f32, mat4x3_af}), nullptr);
+    EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f32, mat4x3_af}), mat4x3_f32);
+    EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f16, mat4x3_af}), mat4x3_f16);
+}
+
+}  // namespace
+}  // namespace tint::sem
diff --git a/src/tint/sem/vector.cc b/src/tint/sem/vector.cc
index 6b4e15d..2df7cf0 100644
--- a/src/tint/sem/vector.cc
+++ b/src/tint/sem/vector.cc
@@ -52,33 +52,17 @@
 }
 
 uint32_t Vector::Size() const {
-    return SizeOf(width_);
+    return subtype_->Size() * width_;
 }
 
 uint32_t Vector::Align() const {
-    return AlignOf(width_);
-}
-
-uint32_t Vector::SizeOf(uint32_t width) {
-    switch (width) {
+    switch (width_) {
         case 2:
-            return 8;
+            return subtype_->Size() * 2;
         case 3:
-            return 12;
+            return subtype_->Size() * 4;
         case 4:
-            return 16;
-    }
-    return 0;  // Unreachable
-}
-
-uint32_t Vector::AlignOf(uint32_t width) {
-    switch (width) {
-        case 2:
-            return 8;
-        case 3:
-            return 16;
-        case 4:
-            return 16;
+            return subtype_->Size() * 4;
     }
     return 0;  // Unreachable
 }
diff --git a/src/tint/symbol_table.cc b/src/tint/symbol_table.cc
index 0a7fa5c..2b20fed 100644
--- a/src/tint/symbol_table.cc
+++ b/src/tint/symbol_table.cc
@@ -34,8 +34,9 @@
     TINT_ASSERT(Symbol, !name.empty());
 
     auto it = name_to_symbol_.find(name);
-    if (it != name_to_symbol_.end())
+    if (it != name_to_symbol_.end()) {
         return it->second;
+    }
 
 #if TINT_SYMBOL_STORE_DEBUG_NAME
     Symbol sym(next_symbol_, program_id_, name);
diff --git a/src/tint/transform/array_length_from_uniform.cc b/src/tint/transform/array_length_from_uniform.cc
index dc19c5c..86c4534 100644
--- a/src/tint/transform/array_length_from_uniform.cc
+++ b/src/tint/transform/array_length_from_uniform.cc
@@ -52,7 +52,7 @@
             continue;
         }
 
-        auto* call = sem.Get(call_expr);
+        auto* call = sem.Get(call_expr)->UnwrapMaterialize()->As<sem::Call>();
         auto* builtin = call->Target()->As<sem::Builtin>();
         if (!builtin || builtin->Type() != sem::BuiltinType::kArrayLength) {
             continue;
diff --git a/src/tint/transform/calculate_array_length.cc b/src/tint/transform/calculate_array_length.cc
index 42a212c..bdda1cd 100644
--- a/src/tint/transform/calculate_array_length.cc
+++ b/src/tint/transform/calculate_array_length.cc
@@ -123,7 +123,7 @@
     // Find all the arrayLength() calls...
     for (auto* node : ctx.src->ASTNodes().Objects()) {
         if (auto* call_expr = node->As<ast::CallExpression>()) {
-            auto* call = sem.Get(call_expr);
+            auto* call = sem.Get(call_expr)->UnwrapMaterialize()->As<sem::Call>();
             if (auto* builtin = call->Target()->As<sem::Builtin>()) {
                 if (builtin->Type() == sem::BuiltinType::kArrayLength) {
                     // We're dealing with an arrayLength() call
diff --git a/src/tint/transform/combine_samplers.cc b/src/tint/transform/combine_samplers.cc
index 66b5b93..c9d4913 100644
--- a/src/tint/transform/combine_samplers.cc
+++ b/src/tint/transform/combine_samplers.cc
@@ -220,7 +220,7 @@
         // sampler parameters to use the current function's combined samplers or
         // the combined global samplers, as appropriate.
         ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::Expression* {
-            if (auto* call = sem.Get(expr)) {
+            if (auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>()) {
                 ast::ExpressionList args;
                 // Replace all texture builtin calls.
                 if (auto* builtin = call->Target()->As<sem::Builtin>()) {
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index 66c6073..775cc05 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -882,7 +882,7 @@
         }
 
         if (auto* call_expr = node->As<ast::CallExpression>()) {
-            auto* call = sem.Get(call_expr);
+            auto* call = sem.Get(call_expr)->UnwrapMaterialize()->As<sem::Call>();
             if (auto* builtin = call->Target()->As<sem::Builtin>()) {
                 if (builtin->Type() == sem::BuiltinType::kArrayLength) {
                     // arrayLength(X)
diff --git a/src/tint/transform/decompose_strided_array.cc b/src/tint/transform/decompose_strided_array.cc
index bf36c06..ba6252b 100644
--- a/src/tint/transform/decompose_strided_array.cc
+++ b/src/tint/transform/decompose_strided_array.cc
@@ -115,7 +115,7 @@
     //   `array<strided_arr, 3>(strided_arr(1), strided_arr(2), strided_arr(3))`
     ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::Expression* {
         if (!expr->args.empty()) {
-            if (auto* call = sem.Get(expr)) {
+            if (auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>()) {
                 if (auto* ctor = call->Target()->As<sem::TypeConstructor>()) {
                     if (auto* arr = ctor->ReturnType()->As<sem::Array>()) {
                         // Begin by cloning the array constructor type or name
diff --git a/src/tint/transform/disable_uniformity_analysis.cc b/src/tint/transform/disable_uniformity_analysis.cc
new file mode 100644
index 0000000..7a30023
--- /dev/null
+++ b/src/tint/transform/disable_uniformity_analysis.cc
@@ -0,0 +1,40 @@
+// 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/disable_uniformity_analysis.h"
+
+#include <utility>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/module.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::DisableUniformityAnalysis);
+
+namespace tint::transform {
+
+DisableUniformityAnalysis::DisableUniformityAnalysis() = default;
+
+DisableUniformityAnalysis::~DisableUniformityAnalysis() = default;
+
+bool DisableUniformityAnalysis::ShouldRun(const Program* program, const DataMap&) const {
+    return !program->Sem().Module()->Extensions().contains(
+        ast::Extension::kChromiumDisableUniformityAnalysis);
+}
+
+void DisableUniformityAnalysis::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+    ctx.dst->Enable(ast::Extension::kChromiumDisableUniformityAnalysis);
+    ctx.Clone();
+}
+
+}  // namespace tint::transform
diff --git a/src/tint/transform/disable_uniformity_analysis.h b/src/tint/transform/disable_uniformity_analysis.h
new file mode 100644
index 0000000..3c9fb53
--- /dev/null
+++ b/src/tint/transform/disable_uniformity_analysis.h
@@ -0,0 +1,47 @@
+// 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_DISABLE_UNIFORMITY_ANALYSIS_H_
+#define SRC_TINT_TRANSFORM_DISABLE_UNIFORMITY_ANALYSIS_H_
+
+#include "src/tint/transform/transform.h"
+
+namespace tint::transform {
+
+/// Disable uniformity analysis for the program.
+class DisableUniformityAnalysis final : public Castable<DisableUniformityAnalysis, Transform> {
+  public:
+    /// Constructor
+    DisableUniformityAnalysis();
+    /// Destructor
+    ~DisableUniformityAnalysis() 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;
+};
+
+}  // namespace tint::transform
+
+#endif  // SRC_TINT_TRANSFORM_DISABLE_UNIFORMITY_ANALYSIS_H_
diff --git a/src/tint/transform/disable_uniformity_analysis_test.cc b/src/tint/transform/disable_uniformity_analysis_test.cc
new file mode 100644
index 0000000..a502442
--- /dev/null
+++ b/src/tint/transform/disable_uniformity_analysis_test.cc
@@ -0,0 +1,73 @@
+// 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/disable_uniformity_analysis.h"
+
+#include <string>
+#include <utility>
+
+#include "src/tint/transform/test_helper.h"
+
+namespace tint::transform {
+namespace {
+
+using DisableUniformityAnalysisTest = TransformTest;
+
+TEST_F(DisableUniformityAnalysisTest, ShouldRunEmptyModule) {
+    auto* src = R"()";
+
+    EXPECT_TRUE(ShouldRun<DisableUniformityAnalysis>(src));
+}
+
+TEST_F(DisableUniformityAnalysisTest, ShouldRunExtensionAlreadyPresent) {
+    auto* src = R"(
+enable chromium_disable_uniformity_analysis;
+)";
+
+    EXPECT_FALSE(ShouldRun<DisableUniformityAnalysis>(src));
+}
+
+TEST_F(DisableUniformityAnalysisTest, EmptyModule) {
+    auto* src = R"()";
+
+    auto* expect = R"(
+enable chromium_disable_uniformity_analysis;
+)";
+
+    auto got = Run<DisableUniformityAnalysis>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(DisableUniformityAnalysisTest, NonEmptyModule) {
+    auto* src = R"(
+@group(0) @binding(0) var<storage, read> global : i32;
+
+@stage(compute) @workgroup_size(64)
+fn main() {
+  if ((global == 42)) {
+    workgroupBarrier();
+  }
+}
+)";
+
+    auto expect = "\nenable chromium_disable_uniformity_analysis;\n" + std::string(src);
+
+    auto got = Run<DisableUniformityAnalysis>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+}  // namespace
+}  // namespace tint::transform
diff --git a/src/tint/transform/fold_constants.cc b/src/tint/transform/fold_constants.cc
index be547b1..d51a68a 100644
--- a/src/tint/transform/fold_constants.cc
+++ b/src/tint/transform/fold_constants.cc
@@ -56,6 +56,21 @@
             return nullptr;
         }
 
+        auto build_scalar = [&](sem::Constant::Scalar s) {
+            return Switch(
+                value.ElementType(),  //
+                [&](const sem::I32*) { return ctx.dst->Expr(i32(std::get<AInt>(s).value)); },
+                [&](const sem::U32*) { return ctx.dst->Expr(u32(std::get<AInt>(s).value)); },
+                [&](const sem::F32*) { return ctx.dst->Expr(f32(std::get<AFloat>(s).value)); },
+                [&](const sem::Bool*) { return ctx.dst->Expr(std::get<bool>(s)); },
+                [&](Default) {
+                    TINT_ICE(Transform, ctx.dst->Diagnostics())
+                        << "unhandled Constant::Scalar type: "
+                        << value.ElementType()->FriendlyName(ctx.src->Symbols());
+                    return nullptr;
+                });
+        };
+
         if (auto* vec = ty->As<sem::Vector>()) {
             uint32_t vec_size = static_cast<uint32_t>(vec->Width());
 
@@ -73,7 +88,7 @@
 
             ast::ExpressionList ctors;
             for (uint32_t i = 0; i < ctor_size; ++i) {
-                value.WithScalarAt(i, [&](auto&& s) { ctors.emplace_back(ctx.dst->Expr(s)); });
+                ctors.emplace_back(build_scalar(value.Elements()[i]));
             }
 
             auto* el_ty = CreateASTTypeFor(ctx, vec->type());
@@ -81,8 +96,7 @@
         }
 
         if (ty->is_scalar()) {
-            return value.WithScalarAt(
-                0, [&](auto&& s) -> const ast::LiteralExpression* { return ctx.dst->Expr(s); });
+            return build_scalar(value.Elements()[0]);
         }
 
         return nullptr;
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index 0bc507e..2a9e20e 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -184,7 +184,8 @@
         // Transform the original textureLoad and textureSampleLevel calls into
         // textureLoadExternal and textureSampleExternal calls.
         ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::CallExpression* {
-            auto* builtin = sem.Get(expr)->Target()->As<sem::Builtin>();
+            auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>();
+            auto* builtin = call->Target()->As<sem::Builtin>();
 
             if (builtin && !builtin->Parameters().empty() &&
                 builtin->Parameters()[0]->Type()->Is<sem::ExternalTexture>() &&
@@ -209,7 +210,7 @@
                     }
                 }
 
-            } else if (sem.Get(expr)->Target()->Is<sem::Function>()) {
+            } else if (call->Target()->Is<sem::Function>()) {
                 // The call expression may be to a user-defined function that
                 // contains a texture_external parameter. These need to be expanded
                 // out to multiple plane textures and the texture parameters
diff --git a/src/tint/transform/promote_initializers_to_const_var.cc b/src/tint/transform/promote_initializers_to_const_var.cc
index 81b5603..6e0ba55 100644
--- a/src/tint/transform/promote_initializers_to_const_var.cc
+++ b/src/tint/transform/promote_initializers_to_const_var.cc
@@ -33,7 +33,7 @@
     // 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);
+        auto* ctor = ctx.src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
         if (!ctor->Target()->Is<sem::TypeConstructor>()) {
             return true;
         }
diff --git a/src/tint/transform/remove_phonies.cc b/src/tint/transform/remove_phonies.cc
index dc5c092..7ca1194 100644
--- a/src/tint/transform/remove_phonies.cc
+++ b/src/tint/transform/remove_phonies.cc
@@ -86,12 +86,19 @@
             if (stmt->lhs->Is<ast::PhonyExpression>()) {
                 std::vector<const ast::Expression*> side_effects;
                 if (!ast::TraverseExpressions(
-                        stmt->rhs, ctx.dst->Diagnostics(), [&](const ast::CallExpression* call) {
+                        stmt->rhs, ctx.dst->Diagnostics(), [&](const ast::CallExpression* expr) {
                             // ast::CallExpression may map to a function or builtin call
                             // (both may have side-effects), or a type constructor or
                             // type conversion (both do not have side effects).
-                            if (sem.Get(call)->Target()->IsAnyOf<sem::Function, sem::Builtin>()) {
-                                side_effects.push_back(call);
+                            auto* call = sem.Get<sem::Call>(expr);
+                            if (!call) {
+                                // Semantic node must be a Materialize, in which case the expression
+                                // was creation-time (compile time), so could not have side effects.
+                                // Just skip.
+                                return ast::TraverseAction::Skip;
+                            }
+                            if (call->Target()->IsAnyOf<sem::Function, sem::Builtin>()) {
+                                side_effects.push_back(expr);
                                 return ast::TraverseAction::Skip;
                             }
                             return ast::TraverseAction::Descend;
diff --git a/src/tint/transform/renamer.cc b/src/tint/transform/renamer.cc
index 50cd781..562a52f 100644
--- a/src/tint/transform/renamer.cc
+++ b/src/tint/transform/renamer.cc
@@ -1278,7 +1278,7 @@
                 }
             }
         } else if (auto* call = node->As<ast::CallExpression>()) {
-            auto* sem = in->Sem().Get(call);
+            auto* sem = in->Sem().Get(call)->UnwrapMaterialize()->As<sem::Call>();
             if (!sem) {
                 TINT_ICE(Transform, out.Diagnostics()) << "CallExpression has no semantic info";
                 continue;
diff --git a/src/tint/transform/robustness.cc b/src/tint/transform/robustness.cc
index a7f2a4a..aab1e0c 100644
--- a/src/tint/transform/robustness.cc
+++ b/src/tint/transform/robustness.cc
@@ -123,10 +123,10 @@
         if (auto idx_constant = idx_sem->ConstantValue()) {
             // Constant value index
             if (idx_constant.Type()->Is<sem::I32>()) {
-                idx.i32 = idx_constant.Elements()[0].i32;
+                idx.i32 = static_cast<int32_t>(idx_constant.Element<AInt>(0).value);
                 idx.is_signed = true;
             } else if (idx_constant.Type()->Is<sem::U32>()) {
-                idx.u32 = idx_constant.Elements()[0].u32;
+                idx.u32 = static_cast<uint32_t>(idx_constant.Element<AInt>(0).value);
                 idx.is_signed = false;
             } else {
                 TINT_ICE(Transform, b.Diagnostics()) << "unsupported constant value for accessor "
@@ -206,7 +206,7 @@
     /// @return the clamped replacement call expression, or nullptr if `expr`
     /// should be cloned without changes.
     const ast::CallExpression* Transform(const ast::CallExpression* expr) {
-        auto* call = ctx.src->Sem().Get(expr);
+        auto* call = ctx.src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
         auto* call_target = call->Target();
         auto* builtin = call_target->As<sem::Builtin>();
         if (!builtin || !TextureBuiltinNeedsClamping(builtin->Type())) {
diff --git a/src/tint/transform/transform.cc b/src/tint/transform/transform.cc
index e3d0ea9..f3ab173 100644
--- a/src/tint/transform/transform.cc
+++ b/src/tint/transform/transform.cc
@@ -87,6 +87,9 @@
     if (ty->Is<sem::U32>()) {
         return ctx.dst->create<ast::U32>();
     }
+    if (ty->Is<sem::F16>()) {
+        return ctx.dst->create<ast::F16>();
+    }
     if (ty->Is<sem::F32>()) {
         return ctx.dst->create<ast::F32>();
     }
diff --git a/src/tint/transform/vectorize_scalar_matrix_constructors.cc b/src/tint/transform/vectorize_scalar_matrix_constructors.cc
index 80e8b1e..9cd9757 100644
--- a/src/tint/transform/vectorize_scalar_matrix_constructors.cc
+++ b/src/tint/transform/vectorize_scalar_matrix_constructors.cc
@@ -14,12 +14,14 @@
 
 #include "src/tint/transform/vectorize_scalar_matrix_constructors.h"
 
+#include <unordered_map>
 #include <utility>
 
 #include "src/tint/program_builder.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"
 
 TINT_INSTANTIATE_TYPEINFO(tint::transform::VectorizeScalarMatrixConstructors);
 
@@ -44,8 +46,10 @@
 }
 
 void VectorizeScalarMatrixConstructors::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+    std::unordered_map<const sem::Matrix*, Symbol> scalar_ctors;
+
     ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::CallExpression* {
-        auto* call = ctx.src->Sem().Get(expr);
+        auto* call = ctx.src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
         auto* ty_ctor = call->Target()->As<sem::TypeConstructor>();
         if (!ty_ctor) {
             return nullptr;
@@ -64,21 +68,56 @@
             return nullptr;
         }
 
-        // Build a list of vector expressions for each column.
-        ast::ExpressionList columns;
-        for (uint32_t c = 0; c < mat_type->columns(); c++) {
-            // Build a list of scalar expressions for each value in the column.
-            ast::ExpressionList row_values;
-            for (uint32_t r = 0; r < mat_type->rows(); r++) {
-                row_values.push_back(ctx.Clone(args[c * mat_type->rows() + r]->Declaration()));
-            }
+        // Constructs a matrix using vector columns, with the elements constructed using the
+        // 'element(uint32_t c, uint32_t r)' callback.
+        auto build_mat = [&](auto&& element) {
+            ast::ExpressionList columns(mat_type->columns());
+            for (uint32_t c = 0; c < mat_type->columns(); c++) {
+                ast::ExpressionList row_values(mat_type->rows());
+                for (uint32_t r = 0; r < mat_type->rows(); r++) {
+                    row_values[r] = element(c, r);
+                }
 
-            // Construct the column vector.
-            auto* col =
-                ctx.dst->vec(CreateASTTypeFor(ctx, mat_type->type()), mat_type->rows(), row_values);
-            columns.push_back(col);
+                // Construct the column vector.
+                columns[c] = ctx.dst->vec(CreateASTTypeFor(ctx, mat_type->type()), mat_type->rows(),
+                                          row_values);
+            }
+            return ctx.dst->Construct(CreateASTTypeFor(ctx, mat_type), columns);
+        };
+
+        if (args.size() == 1) {
+            // Generate a helper function for constructing the matrix.
+            // This is done to ensure that the single argument value is only evaluated once, and
+            // with the correct expression evaluation order.
+            auto fn = utils::GetOrCreate(scalar_ctors, mat_type, [&] {
+                auto name =
+                    ctx.dst->Symbols().New("build_mat" + std::to_string(mat_type->columns()) + "x" +
+                                           std::to_string(mat_type->rows()));
+                ctx.dst->Func(name,
+                              {
+                                  // Single scalar parameter
+                                  ctx.dst->Param("value", CreateASTTypeFor(ctx, mat_type->type())),
+                              },
+                              CreateASTTypeFor(ctx, mat_type),
+                              {
+                                  ctx.dst->Return(build_mat([&](uint32_t, uint32_t) {  //
+                                      return ctx.dst->Expr("value");
+                                  })),
+                              });
+                return name;
+            });
+            return ctx.dst->Call(fn, ctx.Clone(args[0]->Declaration()));
         }
-        return ctx.dst->Construct(CreateASTTypeFor(ctx, mat_type), columns);
+
+        if (args.size() == mat_type->columns() * mat_type->rows()) {
+            return build_mat([&](uint32_t c, uint32_t r) {
+                return ctx.Clone(args[c * mat_type->rows() + r]->Declaration());
+            });
+        }
+
+        TINT_ICE(Transform, ctx.dst->Diagnostics())
+            << "matrix constructor has unexpected number of arguments";
+        return nullptr;
     });
 
     ctx.Clone();
diff --git a/src/tint/transform/vectorize_scalar_matrix_constructors_test.cc b/src/tint/transform/vectorize_scalar_matrix_constructors_test.cc
index 242151a..2b1c098 100644
--- a/src/tint/transform/vectorize_scalar_matrix_constructors_test.cc
+++ b/src/tint/transform/vectorize_scalar_matrix_constructors_test.cc
@@ -31,7 +31,57 @@
     EXPECT_FALSE(ShouldRun<VectorizeScalarMatrixConstructors>(src));
 }
 
-TEST_P(VectorizeScalarMatrixConstructorsTest, Basic) {
+TEST_P(VectorizeScalarMatrixConstructorsTest, SingleScalars) {
+    uint32_t cols = GetParam().first;
+    uint32_t rows = GetParam().second;
+    std::string matrix_no_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows);
+    std::string matrix = matrix_no_type + "<f32>";
+    std::string vector = "vec" + std::to_string(rows) + "<f32>";
+    std::string values;
+    for (uint32_t c = 0; c < cols; c++) {
+        if (c > 0) {
+            values += ", ";
+        }
+        values += vector + "(";
+        for (uint32_t r = 0; r < rows; r++) {
+            if (r > 0) {
+                values += ", ";
+            }
+            values += "value";
+        }
+        values += ")";
+    }
+
+    std::string src = R"(
+@stage(fragment)
+fn main() {
+  let m = ${matrix}(42.0);
+}
+)";
+
+    std::string expect = R"(
+fn build_${matrix_no_type}(value : f32) -> ${matrix} {
+  return ${matrix}(${values});
+}
+
+@stage(fragment)
+fn main() {
+  let m = build_${matrix_no_type}(42.0);
+}
+)";
+    src = utils::ReplaceAll(src, "${matrix}", matrix);
+    expect = utils::ReplaceAll(expect, "${matrix}", matrix);
+    expect = utils::ReplaceAll(expect, "${matrix_no_type}", matrix_no_type);
+    expect = utils::ReplaceAll(expect, "${values}", values);
+
+    EXPECT_TRUE(ShouldRun<VectorizeScalarMatrixConstructors>(src));
+
+    auto got = Run<VectorizeScalarMatrixConstructors>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(VectorizeScalarMatrixConstructorsTest, MultipleScalars) {
     uint32_t cols = GetParam().first;
     uint32_t rows = GetParam().second;
     std::string mat_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows) + "<f32>";
diff --git a/src/tint/transform/wrap_arrays_in_structs.cc b/src/tint/transform/wrap_arrays_in_structs.cc
index b1dc5e8..eb133d7 100644
--- a/src/tint/transform/wrap_arrays_in_structs.cc
+++ b/src/tint/transform/wrap_arrays_in_structs.cc
@@ -83,7 +83,7 @@
 
     // 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)) {
+        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)) {
diff --git a/src/tint/transform/zero_init_workgroup_memory.cc b/src/tint/transform/zero_init_workgroup_memory.cc
index 21a4565..6e84310 100644
--- a/src/tint/transform/zero_init_workgroup_memory.cc
+++ b/src/tint/transform/zero_init_workgroup_memory.cc
@@ -359,13 +359,8 @@
             }
             auto* sem = ctx.src->Sem().Get(expr);
             if (auto c = sem->ConstantValue()) {
-                if (c.ElementType()->Is<sem::I32>()) {
-                    workgroup_size_const *= static_cast<uint32_t>(c.Elements()[0].i32);
-                    continue;
-                } else if (c.ElementType()->Is<sem::U32>()) {
-                    workgroup_size_const *= c.Elements()[0].u32;
-                    continue;
-                }
+                workgroup_size_const *= c.Element<AInt>(0).value;
+                continue;
             }
             // Constant value could not be found. Build expression instead.
             workgroup_size_expr = [this, expr, size = workgroup_size_expr] {
diff --git a/src/tint/utils/hash.h b/src/tint/utils/hash.h
index 1bc2edb..3deb14b 100644
--- a/src/tint/utils/hash.h
+++ b/src/tint/utils/hash.h
@@ -18,6 +18,7 @@
 #include <stdint.h>
 #include <cstdio>
 #include <functional>
+#include <tuple>
 #include <utility>
 #include <vector>
 
@@ -45,6 +46,10 @@
 
 }  // namespace detail
 
+// Forward declaration
+template <typename... ARGS>
+size_t Hash(const ARGS&... args);
+
 /// HashCombine "hashes" together an existing hash and hashable values.
 template <typename T>
 void HashCombine(size_t* hash, const T& value) {
@@ -62,6 +67,13 @@
 }
 
 /// HashCombine "hashes" together an existing hash and hashable values.
+template <typename... TYPES>
+void HashCombine(size_t* hash, const std::tuple<TYPES...>& tuple) {
+    HashCombine(hash, sizeof...(TYPES));
+    HashCombine(hash, std::apply(Hash<TYPES...>, tuple));
+}
+
+/// HashCombine "hashes" together an existing hash and hashable values.
 template <typename T, typename... ARGS>
 void HashCombine(size_t* hash, const T& value, const ARGS&... args) {
     HashCombine(hash, value);
diff --git a/src/tint/utils/hash_test.cc b/src/tint/utils/hash_test.cc
index 068ac45..cb74df9 100644
--- a/src/tint/utils/hash_test.cc
+++ b/src/tint/utils/hash_test.cc
@@ -15,6 +15,7 @@
 #include "src/tint/utils/hash.h"
 
 #include <string>
+#include <tuple>
 #include <unordered_map>
 
 #include "gtest/gtest.h"
@@ -41,6 +42,13 @@
     EXPECT_NE(Hash(std::vector<int>({1, 2, 3})), Hash(std::vector<int>({1, 2, 3, 4})));
 }
 
+TEST(HashTests, Tuple) {
+    EXPECT_EQ(Hash(std::make_tuple(1)), Hash(std::make_tuple(1)));
+    EXPECT_EQ(Hash(std::make_tuple(1, 2, 3)), Hash(std::make_tuple(1, 2, 3)));
+    EXPECT_NE(Hash(std::make_tuple(1, 2, 3)), Hash(std::make_tuple(1, 2, 4)));
+    EXPECT_NE(Hash(std::make_tuple(1, 2, 3)), Hash(std::make_tuple(1, 2, 3, 4)));
+}
+
 TEST(HashTests, UnorderedKeyWrapper) {
     using W = UnorderedKeyWrapper<std::vector<int>>;
 
diff --git a/src/tint/utils/unique_vector.h b/src/tint/utils/unique_vector.h
index 96d9cbf..0f3f18d 100644
--- a/src/tint/utils/unique_vector.h
+++ b/src/tint/utils/unique_vector.h
@@ -75,6 +75,9 @@
     /// @returns the number of items in the vector
     size_t size() const { return vector.size(); }
 
+    /// @returns the pointer to the first element in the vector, or nullptr if the vector is empty.
+    const T* data() const { return vector.empty() ? nullptr : vector.data(); }
+
     /// @returns an iterator to the beginning of the vector
     ConstIterator begin() const { return vector.begin(); }
 
diff --git a/src/tint/utils/unique_vector_test.cc b/src/tint/utils/unique_vector_test.cc
index 7d586e9..035ebf8 100644
--- a/src/tint/utils/unique_vector_test.cc
+++ b/src/tint/utils/unique_vector_test.cc
@@ -139,5 +139,14 @@
     EXPECT_EQ(unique_vec.empty(), true);
 }
 
+TEST(UniqueVectorTest, Data) {
+    UniqueVector<int> unique_vec;
+    EXPECT_EQ(unique_vec.data(), nullptr);
+
+    unique_vec.add(42);
+    EXPECT_EQ(unique_vec.data(), &unique_vec[0]);
+    EXPECT_EQ(*unique_vec.data(), 42);
+}
+
 }  // namespace
 }  // namespace tint::utils
diff --git a/src/tint/writer/append_vector_test.cc b/src/tint/writer/append_vector_test.cc
index 46a135f..8169039 100644
--- a/src/tint/writer/append_vector_test.cc
+++ b/src/tint/writer/append_vector_test.cc
@@ -46,7 +46,7 @@
     EXPECT_EQ(vec_123->args[1], scalar_2);
     EXPECT_EQ(vec_123->args[2], scalar_3);
 
-    auto* call = Sem().Get(vec_123);
+    auto* call = Sem().Get<sem::Call>(vec_123);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 3u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
@@ -90,7 +90,7 @@
     ASSERT_EQ(u32_to_i32->args.size(), 1u);
     EXPECT_EQ(u32_to_i32->args[0], scalar_3);
 
-    auto* call = Sem().Get(vec_123);
+    auto* call = Sem().Get<sem::Call>(vec_123);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 3u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
@@ -142,7 +142,7 @@
     ASSERT_EQ(u32_to_i32->args.size(), 1u);
     EXPECT_EQ(u32_to_i32->args[0], scalar_3);
 
-    auto* call = Sem().Get(vec_123);
+    auto* call = Sem().Get<sem::Call>(vec_123);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 2u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
@@ -184,7 +184,7 @@
     ASSERT_EQ(f32_to_i32->args.size(), 1u);
     EXPECT_EQ(f32_to_i32->args[0], scalar_3);
 
-    auto* call = Sem().Get(vec_123);
+    auto* call = Sem().Get<sem::Call>(vec_123);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 3u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
@@ -226,7 +226,7 @@
     EXPECT_EQ(vec_1234->args[2], scalar_3);
     EXPECT_EQ(vec_1234->args[3], scalar_4);
 
-    auto* call = Sem().Get(vec_1234);
+    auto* call = Sem().Get<sem::Call>(vec_1234);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 4u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
@@ -266,7 +266,7 @@
     EXPECT_EQ(vec_123->args[0], vec_12);
     EXPECT_EQ(vec_123->args[1], scalar_3);
 
-    auto* call = Sem().Get(vec_123);
+    auto* call = Sem().Get<sem::Call>(vec_123);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 2u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
@@ -305,7 +305,7 @@
     EXPECT_EQ(vec_123->args[1], scalar_2);
     EXPECT_EQ(vec_123->args[2], scalar_3);
 
-    auto* call = Sem().Get(vec_123);
+    auto* call = Sem().Get<sem::Call>(vec_123);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 3u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
@@ -344,7 +344,7 @@
     EXPECT_EQ(vec_123->args[0], vec_12);
     EXPECT_EQ(vec_123->args[1], scalar_3);
 
-    auto* call = Sem().Get(vec_123);
+    auto* call = Sem().Get<sem::Call>(vec_123);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 2u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
@@ -385,7 +385,7 @@
     ASSERT_EQ(f32_to_i32->args.size(), 1u);
     EXPECT_EQ(f32_to_i32->args[0], scalar_3);
 
-    auto* call = Sem().Get(vec_123);
+    auto* call = Sem().Get<sem::Call>(vec_123);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 2u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
@@ -422,7 +422,7 @@
     EXPECT_EQ(vec_123->args[0], vec_12);
     EXPECT_EQ(vec_123->args[1], scalar_3);
 
-    auto* call = Sem().Get(vec_123);
+    auto* call = Sem().Get<sem::Call>(vec_123);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 2u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
@@ -461,7 +461,7 @@
     }
     EXPECT_EQ(vec_0004->args[3], scalar);
 
-    auto* call = Sem().Get(vec_0004);
+    auto* call = Sem().Get<sem::Call>(vec_0004);
     ASSERT_NE(call, nullptr);
     ASSERT_EQ(call->Arguments().size(), 4u);
     EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_0004->args[0]));
diff --git a/src/tint/writer/glsl/generator.cc b/src/tint/writer/glsl/generator.cc
index 4b6da68..4b1e516 100644
--- a/src/tint/writer/glsl/generator.cc
+++ b/src/tint/writer/glsl/generator.cc
@@ -30,6 +30,10 @@
 
 Result Generate(const Program* program, const Options& options, const std::string& entry_point) {
     Result result;
+    if (!program->IsValid()) {
+        result.error = "input program is not valid";
+        return result;
+    }
 
     // Sanitize the program.
     auto sanitized_result = Sanitize(program, options, entry_point);
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index f52862b..1cf7480 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -35,6 +35,7 @@
 #include "src/tint/sem/depth_multisampled_texture.h"
 #include "src/tint/sem/depth_texture.h"
 #include "src/tint/sem/function.h"
+#include "src/tint/sem/materialize.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/module.h"
 #include "src/tint/sem/multisampled_texture.h"
@@ -52,6 +53,7 @@
 #include "src/tint/transform/canonicalize_entry_point_io.h"
 #include "src/tint/transform/combine_samplers.h"
 #include "src/tint/transform/decompose_memory_access.h"
+#include "src/tint/transform/disable_uniformity_analysis.h"
 #include "src/tint/transform/expand_compound_assignment.h"
 #include "src/tint/transform/fold_trivial_single_use_lets.h"
 #include "src/tint/transform/loop_to_for_loop.h"
@@ -157,6 +159,8 @@
     transform::Manager manager;
     transform::DataMap data;
 
+    manager.Add<transform::DisableUniformityAnalysis>();
+
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.count_leading_zeros = true;
@@ -687,7 +691,12 @@
 }
 
 bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
-    auto* call = builder_.Sem().Get(expr);
+    auto* sem = builder_.Sem().Get(expr);
+    if (auto* m = sem->As<sem::Materialize>()) {
+        // TODO(crbug.com/tint/1504): Just emit the constant value.
+        sem = m->Expr();
+    }
+    auto* call = sem->As<sem::Call>();
     auto* target = call->Target();
 
     if (target->Is<sem::Function>()) {
@@ -1461,8 +1470,9 @@
 
     out << "(";
 
-    if (!EmitExpression(out, texture))
+    if (!EmitExpression(out, texture)) {
         return false;
+    }
 
     out << ", ";
 
@@ -2562,6 +2572,9 @@
         out << "bool";
     } else if (type->Is<sem::F32>()) {
         out << "float";
+    } else if (type->Is<sem::F16>()) {
+        diagnostics_.add_error(diag::System::Writer, "Type f16 is not completely implemented yet.");
+        return false;
     } else if (type->Is<sem::I32>()) {
         out << "int";
     } else if (auto* mat = type->As<sem::Matrix>()) {
diff --git a/src/tint/writer/glsl/generator_impl_builtin_test.cc b/src/tint/writer/glsl/generator_impl_builtin_test.cc
index 1d80e6a..7942ed5 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_test.cc
@@ -169,7 +169,7 @@
 
     GeneratorImpl& gen = Build();
 
-    auto* sem = program->Sem().Get(call);
+    auto* sem = program->Sem().Get<sem::Call>(call);
     ASSERT_NE(sem, nullptr);
     auto* target = sem->Target();
     ASSERT_NE(target, nullptr);
diff --git a/src/tint/writer/glsl/generator_impl_test.cc b/src/tint/writer/glsl/generator_impl_test.cc
index ad11413..af2e205 100644
--- a/src/tint/writer/glsl/generator_impl_test.cc
+++ b/src/tint/writer/glsl/generator_impl_test.cc
@@ -19,6 +19,15 @@
 
 using GlslGeneratorImplTest = TestHelper;
 
+TEST_F(GlslGeneratorImplTest, InvalidProgram) {
+    Diagnostics().add_error(diag::System::Writer, "make the program invalid");
+    ASSERT_FALSE(IsValid());
+    auto program = std::make_unique<Program>(std::move(*this));
+    ASSERT_FALSE(program->IsValid());
+    auto result = Generate(program.get(), Options{}, "");
+    EXPECT_EQ(result.error, "input program is not valid");
+}
+
 TEST_F(GlslGeneratorImplTest, Generate) {
     Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
 
diff --git a/src/tint/writer/hlsl/generator.cc b/src/tint/writer/hlsl/generator.cc
index ff3ffbe..b514c2b 100644
--- a/src/tint/writer/hlsl/generator.cc
+++ b/src/tint/writer/hlsl/generator.cc
@@ -29,6 +29,10 @@
 
 Result Generate(const Program* program, const Options& options) {
     Result result;
+    if (!program->IsValid()) {
+        result.error = "input program is not valid";
+        return result;
+    }
 
     // Sanitize the program.
     auto sanitized_result = Sanitize(program, options);
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 7ee5337..affaf5e 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -36,6 +36,7 @@
 #include "src/tint/sem/depth_multisampled_texture.h"
 #include "src/tint/sem/depth_texture.h"
 #include "src/tint/sem/function.h"
+#include "src/tint/sem/materialize.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/module.h"
 #include "src/tint/sem/multisampled_texture.h"
@@ -52,6 +53,7 @@
 #include "src/tint/transform/calculate_array_length.h"
 #include "src/tint/transform/canonicalize_entry_point_io.h"
 #include "src/tint/transform/decompose_memory_access.h"
+#include "src/tint/transform/disable_uniformity_analysis.h"
 #include "src/tint/transform/expand_compound_assignment.h"
 #include "src/tint/transform/fold_trivial_single_use_lets.h"
 #include "src/tint/transform/localize_struct_array_assignment.h"
@@ -65,6 +67,7 @@
 #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/zero_init_workgroup_memory.h"
 #include "src/tint/utils/defer.h"
 #include "src/tint/utils/map.h"
@@ -139,6 +142,8 @@
     transform::Manager manager;
     transform::DataMap data;
 
+    manager.Add<transform::DisableUniformityAnalysis>();
+
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
         // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
@@ -195,6 +200,7 @@
     manager.Add<transform::ExpandCompoundAssignment>();
     manager.Add<transform::PromoteSideEffectsToDecl>();
     manager.Add<transform::UnwindDiscardFunctions>();
+    manager.Add<transform::VectorizeScalarMatrixConstructors>();
     manager.Add<transform::SimplifyPointers>();
     manager.Add<transform::RemovePhonies>();
     // ArrayLengthFromUniform must come after InlinePointerLets and Simplify, as
@@ -246,6 +252,10 @@
         if (decl->Is<ast::Alias>()) {
             continue;  // Ignore aliases.
         }
+        if (decl->Is<ast::Enable>()) {
+            // Currently we don't have to do anything for using a extension in HLSL.
+            continue;
+        }
 
         // Emit a new line between declarations if the type of declaration has
         // changed, or we're about to emit a function
@@ -285,11 +295,6 @@
                 }
                 return EmitFunction(func);
             },
-            [&](const ast::Enable*) {
-                // Currently we don't have to do anything for using a extension in
-                // HLSL
-                return true;
-            },
             [&](Default) {
                 TINT_ICE(Writer, diagnostics_)
                     << "unhandled module-scope declaration: " << decl->TypeInfo().name;
@@ -656,13 +661,8 @@
                 if (i != 0) {
                     out << ", ";
                 }
-                if (!val.WithScalarAt(i, [&](auto&& s) -> bool {
-                        // Use std::equal_to to work around -Wfloat-equal warnings
-                        using T = std::remove_reference_t<decltype(s)>;
-                        auto equal_to = std::equal_to<T>{};
-                        bool is_zero = equal_to(s, T(0));
-                        return EmitValue(out, elem_ty, is_zero ? 1 : static_cast<int>(s));
-                    })) {
+                auto s = val.Element<AInt>(i).value;
+                if (!EmitValue(out, elem_ty, (s == 0) ? 1 : static_cast<int>(s))) {
                     return false;
                 }
             }
@@ -925,7 +925,12 @@
 }
 
 bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
-    auto* call = builder_.Sem().Get(expr);
+    auto* sem = builder_.Sem().Get(expr);
+    if (auto* m = sem->As<sem::Materialize>()) {
+        // TODO(crbug.com/tint/1504): Just emit the constant value.
+        sem = m->Expr();
+    }
+    auto* call = sem->As<sem::Call>();
     auto* target = call->Target();
     return Switch(
         target, [&](const sem::Function* func) { return EmitFunctionCall(out, call, func); },
@@ -994,23 +999,25 @@
 bool GeneratorImpl::EmitBuiltinCall(std::ostream& out,
                                     const sem::Call* call,
                                     const sem::Builtin* builtin) {
+    const auto type = builtin->Type();
+
     auto* expr = call->Declaration();
     if (builtin->IsTexture()) {
         return EmitTextureCall(out, call, builtin);
     }
-    if (builtin->Type() == sem::BuiltinType::kSelect) {
+    if (type == sem::BuiltinType::kSelect) {
         return EmitSelectCall(out, expr);
     }
-    if (builtin->Type() == sem::BuiltinType::kModf) {
+    if (type == sem::BuiltinType::kModf) {
         return EmitModfCall(out, expr, builtin);
     }
-    if (builtin->Type() == sem::BuiltinType::kFrexp) {
+    if (type == sem::BuiltinType::kFrexp) {
         return EmitFrexpCall(out, expr, builtin);
     }
-    if (builtin->Type() == sem::BuiltinType::kDegrees) {
+    if (type == sem::BuiltinType::kDegrees) {
         return EmitDegreesCall(out, expr, builtin);
     }
-    if (builtin->Type() == sem::BuiltinType::kRadians) {
+    if (type == sem::BuiltinType::kRadians) {
         return EmitRadiansCall(out, expr, builtin);
     }
     if (builtin->IsDataPacking()) {
@@ -1025,11 +1032,30 @@
     if (builtin->IsAtomic()) {
         return EmitWorkgroupAtomicCall(out, expr, builtin);
     }
+    if (builtin->IsDP4a()) {
+        return EmitDP4aCall(out, expr, builtin);
+    }
+
     auto name = generate_builtin_name(builtin);
     if (name.empty()) {
         return false;
     }
 
+    // Handle single argument builtins that only accept and return uint (not int overload). We need
+    // to explicitly cast the return value (we also cast the arg for good measure). See
+    // crbug.com/tint/1550
+    if (type == sem::BuiltinType::kCountOneBits || type == sem::BuiltinType::kReverseBits) {
+        auto* arg = call->Arguments()[0];
+        if (arg->Type()->UnwrapRef()->is_signed_scalar_or_vector()) {
+            out << "asint(" << name << "(asuint(";
+            if (!EmitExpression(out, arg->Declaration())) {
+                return false;
+            }
+            out << ")))";
+            return true;
+        }
+    }
+
     out << name << "(";
 
     bool first = true;
@@ -1045,6 +1071,7 @@
     }
 
     out << ")";
+
     return true;
 }
 
@@ -1075,6 +1102,55 @@
         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;
+        }
+    }
+
     bool brackets = type->IsAnyOf<sem::Array, sem::Struct>();
 
     // For single-value vector initializers, swizzle the scalar to the right
@@ -1135,7 +1211,7 @@
 
     if (auto val = offset_arg->ConstantValue()) {
         TINT_ASSERT(Writer, val.Type()->Is<sem::U32>());
-        scalar_offset_value = val.Elements()[0].u32;
+        scalar_offset_value = static_cast<uint32_t>(val.Element<AInt>(0).value);
         scalar_offset_value /= 4;  // bytes -> scalar index
         scalar_offset_constant = true;
     }
@@ -2031,6 +2107,34 @@
         });
 }
 
+bool GeneratorImpl::EmitDP4aCall(std::ostream& out,
+                                 const ast::CallExpression* expr,
+                                 const sem::Builtin* builtin) {
+    // TODO(crbug.com/tint/1497): support the polyfill version of DP4a functions.
+    return CallBuiltinHelper(
+        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
+            std::string functionName;
+            switch (builtin->Type()) {
+                case sem::BuiltinType::kDot4I8Packed:
+                    line(b) << "int accumulator = 0;";
+                    functionName = "dot4add_i8packed";
+                    break;
+                case sem::BuiltinType::kDot4U8Packed:
+                    line(b) << "uint accumulator = 0u;";
+                    functionName = "dot4add_u8packed";
+                    break;
+                default:
+                    diagnostics_.add_error(diag::System::Writer,
+                                           "Internal error: unhandled DP4a builtin");
+                    return false;
+            }
+            line(b) << "return " << functionName << "(" << params[0] << ", " << params[1]
+                    << ", accumulator);";
+
+            return true;
+        });
+}
+
 bool GeneratorImpl::EmitBarrierCall(std::ostream& out, const sem::Builtin* builtin) {
     // TODO(crbug.com/tint/661): Combine sequential barriers to a single
     // instruction.
@@ -2245,8 +2349,9 @@
             break;
     }
 
-    if (!EmitExpression(out, texture))
+    if (!EmitExpression(out, texture)) {
         return false;
+    }
 
     // If pack_level_in_coords is true, then the mip level will be appended as the
     // last value of the coordinates argument. If the WGSL builtin overload does
@@ -2287,7 +2392,7 @@
         case sem::BuiltinType::kTextureGather:
             out << ".Gather";
             if (builtin->Parameters()[0]->Usage() == sem::ParameterUsage::kComponent) {
-                switch (call->Arguments()[0]->ConstantValue().Elements()[0].i32) {
+                switch (call->Arguments()[0]->ConstantValue().Element<AInt>(0).value) {
                     case 0:
                         out << "Red";
                         break;
@@ -2318,8 +2423,9 @@
     }
 
     if (auto* sampler = arg(Usage::kSampler)) {
-        if (!EmitExpression(out, sampler))
+        if (!EmitExpression(out, sampler)) {
             return false;
+        }
         out << ", ";
     }
 
@@ -2459,7 +2565,7 @@
         case sem::BuiltinType::kTranspose:
         case sem::BuiltinType::kTrunc:
             return builtin->str();
-        case sem::BuiltinType::kCountOneBits:
+        case sem::BuiltinType::kCountOneBits:  // uint
             return "countbits";
         case sem::BuiltinType::kDpdx:
             return "ddx";
@@ -2487,7 +2593,7 @@
             return "rsqrt";
         case sem::BuiltinType::kMix:
             return "lerp";
-        case sem::BuiltinType::kReverseBits:
+        case sem::BuiltinType::kReverseBits:  // uint
             return "reversebits";
         case sem::BuiltinType::kSmoothstep:
         case sem::BuiltinType::kSmoothStep:
@@ -3482,6 +3588,11 @@
             out << "float";
             return true;
         },
+        [&](const sem::F16*) {
+            diagnostics_.add_error(diag::System::Writer,
+                                   "Type f16 is not completely implemented yet.");
+            return false;
+        },
         [&](const sem::I32*) {
             out << "int";
             return true;
diff --git a/src/tint/writer/hlsl/generator_impl.h b/src/tint/writer/hlsl/generator_impl.h
index e329638..86bbd7d 100644
--- a/src/tint/writer/hlsl/generator_impl.h
+++ b/src/tint/writer/hlsl/generator_impl.h
@@ -242,7 +242,7 @@
     /// Handles generating a call to data packing builtin
     /// @param out the output of the expression stream
     /// @param expr the call expression
-    /// @param builtin the semantic information for the texture builtin
+    /// @param builtin the semantic information for the builtin
     /// @returns true if the call expression is emitted
     bool EmitDataPackingCall(std::ostream& out,
                              const ast::CallExpression* expr,
@@ -250,11 +250,19 @@
     /// Handles generating a call to data unpacking builtin
     /// @param out the output of the expression stream
     /// @param expr the call expression
-    /// @param builtin the semantic information for the texture builtin
+    /// @param builtin the semantic information for the builtin
     /// @returns true if the call expression is emitted
     bool EmitDataUnpackingCall(std::ostream& out,
                                const ast::CallExpression* expr,
                                const sem::Builtin* builtin);
+    /// Handles generating a call to DP4a builtins (dot4I8Packed and dot4U8Packed)
+    /// @param out the output of the expression stream
+    /// @param expr the call expression
+    /// @param builtin the semantic information for the builtin
+    /// @returns true if the call expression is emitted
+    bool EmitDP4aCall(std::ostream& out,
+                      const ast::CallExpression* expr,
+                      const sem::Builtin* builtin);
     /// Handles a case statement
     /// @param s the switch statement
     /// @param case_idx the index of the switch case in the switch statement
@@ -504,6 +512,7 @@
     TextBuffer helpers_;  // Helper functions emitted at the top of the output
     std::function<bool()> emit_continuing_;
     std::unordered_map<DMAIntrinsic, std::string, DMAIntrinsic::Hasher> dma_intrinsics_;
+    std::unordered_map<const sem::Matrix*, std::string> matrix_scalar_ctors_;
     std::unordered_map<const sem::Builtin*, std::string> builtins_;
     std::unordered_map<const sem::Struct*, std::string> structure_builders_;
     std::unordered_map<const sem::Vector*, std::string> dynamic_vector_write_;
diff --git a/src/tint/writer/hlsl/generator_impl_builtin_test.cc b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
index e4e6ba9..40f550b 100644
--- a/src/tint/writer/hlsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
@@ -168,7 +168,7 @@
 
     GeneratorImpl& gen = Build();
 
-    auto* sem = program->Sem().Get(call);
+    auto* sem = program->Sem().Get<sem::Call>(call);
     ASSERT_NE(sem, nullptr);
     auto* target = sem->Target();
     ASSERT_NE(target, nullptr);
@@ -726,5 +726,91 @@
 )");
 }
 
+TEST_F(HlslGeneratorImplTest_Builtin, Dot4I8Packed) {
+    Enable(ast::Extension::kChromiumExperimentalDP4a);
+
+    auto* val1 = Var("val1", ty.u32());
+    auto* val2 = Var("val2", ty.u32());
+    auto* call = Call("dot4I8Packed", val1, val2);
+    WrapInFunction(val1, val2, call);
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(int tint_dot4I8Packed(uint param_0, uint param_1) {
+  int accumulator = 0;
+  return dot4add_i8packed(param_0, param_1, accumulator);
+}
+
+[numthreads(1, 1, 1)]
+void test_function() {
+  uint val1 = 0u;
+  uint val2 = 0u;
+  const int tint_symbol = tint_dot4I8Packed(val1, val2);
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Dot4U8Packed) {
+    Enable(ast::Extension::kChromiumExperimentalDP4a);
+
+    auto* val1 = Var("val1", ty.u32());
+    auto* val2 = Var("val2", ty.u32());
+    auto* call = Call("dot4U8Packed", val1, val2);
+    WrapInFunction(val1, val2, call);
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(uint tint_dot4U8Packed(uint param_0, uint param_1) {
+  uint accumulator = 0u;
+  return dot4add_u8packed(param_0, param_1, accumulator);
+}
+
+[numthreads(1, 1, 1)]
+void test_function() {
+  uint val1 = 0u;
+  uint val2 = 0u;
+  const uint tint_symbol = tint_dot4U8Packed(val1, val2);
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, CountOneBits) {
+    auto* val = Var("val1", ty.i32());
+    auto* call = Call("countOneBits", val);
+    WrapInFunction(val, call);
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
+void test_function() {
+  int val1 = 0;
+  const int tint_symbol = asint(countbits(asuint(val1)));
+  return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, ReverseBits) {
+    auto* val = Var("val1", ty.i32());
+    auto* call = Call("reverseBits", val);
+    WrapInFunction(val, call);
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"([numthreads(1, 1, 1)]
+void test_function() {
+  int val1 = 0;
+  const int tint_symbol = asint(reversebits(asuint(val1)));
+  return;
+}
+)");
+}
+
 }  // namespace
 }  // namespace tint::writer::hlsl
diff --git a/src/tint/writer/hlsl/generator_impl_test.cc b/src/tint/writer/hlsl/generator_impl_test.cc
index 460b202..a1e0edb 100644
--- a/src/tint/writer/hlsl/generator_impl_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_test.cc
@@ -19,6 +19,15 @@
 
 using HlslGeneratorImplTest = TestHelper;
 
+TEST_F(HlslGeneratorImplTest, InvalidProgram) {
+    Diagnostics().add_error(diag::System::Writer, "make the program invalid");
+    ASSERT_FALSE(IsValid());
+    auto program = std::make_unique<Program>(std::move(*this));
+    ASSERT_FALSE(program->IsValid());
+    auto result = Generate(program.get(), Options{});
+    EXPECT_EQ(result.error, "input program is not valid");
+}
+
 TEST_F(HlslGeneratorImplTest, Generate) {
     Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
 
diff --git a/src/tint/writer/msl/generator.cc b/src/tint/writer/msl/generator.cc
index 6d09a86..5cd9ac8 100644
--- a/src/tint/writer/msl/generator.cc
+++ b/src/tint/writer/msl/generator.cc
@@ -31,6 +31,10 @@
 
 Result Generate(const Program* program, const Options& options) {
     Result result;
+    if (!program->IsValid()) {
+        result.error = "input program is not valid";
+        return result;
+    }
 
     // Sanitize the program.
     auto sanitized_result = Sanitize(program, options);
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 8f5800c..be94e2c 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -41,6 +41,7 @@
 #include "src/tint/sem/f32.h"
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/i32.h"
+#include "src/tint/sem/materialize.h"
 #include "src/tint/sem/matrix.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/module.h"
@@ -59,6 +60,7 @@
 #include "src/tint/transform/array_length_from_uniform.h"
 #include "src/tint/transform/builtin_polyfill.h"
 #include "src/tint/transform/canonicalize_entry_point_io.h"
+#include "src/tint/transform/disable_uniformity_analysis.h"
 #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"
@@ -121,6 +123,8 @@
     transform::Manager manager;
     transform::DataMap data;
 
+    manager.Add<transform::DisableUniformityAnalysis>();
+
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
@@ -510,6 +514,22 @@
         return true;
     }
 
+    // Handle '&' and '|' of booleans.
+    if ((expr->IsAnd() || expr->IsOr()) && lhs_type->Is<sem::Bool>()) {
+        out << "bool";
+        ScopedParen sp(out);
+        if (!EmitExpression(out, expr->lhs)) {
+            return false;
+        }
+        if (!emit_op()) {
+            return false;
+        }
+        if (!EmitExpression(out, expr->rhs)) {
+            return false;
+        }
+        return true;
+    }
+
     // Emit as usual
     ScopedParen sp(out);
     if (!EmitExpression(out, expr->lhs)) {
@@ -531,7 +551,12 @@
 }
 
 bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
-    auto* call = program_->Sem().Get(expr);
+    auto* sem = program_->Sem().Get(expr);
+    if (auto* m = sem->As<sem::Materialize>()) {
+        // TODO(crbug.com/tint/1504): Just emit the constant value.
+        sem = m->Expr();
+    }
+    auto* call = sem->As<sem::Call>();
     auto* target = call->Target();
     return Switch(
         target, [&](const sem::Function* func) { return EmitFunctionCall(out, call, func); },
@@ -1024,8 +1049,9 @@
                 }
             }
 
-            if (!EmitExpression(out, e->Declaration()))
+            if (!EmitExpression(out, e->Declaration())) {
                 return false;
+            }
 
             if (casted) {
                 out << ")";
@@ -1117,8 +1143,8 @@
                     break;  // Other texture dimensions don't have an offset
             }
         }
-        auto c = component->ConstantValue().Elements()[0].i32;
-        switch (c) {
+        auto c = component->ConstantValue().Element<AInt>(0);
+        switch (c.value) {
             case 0:
                 out << "component::x";
                 break;
@@ -1464,6 +1490,12 @@
             out << "false";
             return true;
         },
+        [&](const sem::F16*) {
+            // Placeholder for emitting f16 zero value
+            diagnostics_.add_error(diag::System::Writer,
+                                   "Type f16 is not completely implemented yet");
+            return false;
+        },
         [&](const sem::F32*) {
             out << "0.0f";
             return true;
@@ -2236,6 +2268,11 @@
             out << "bool";
             return true;
         },
+        [&](const sem::F16*) {
+            diagnostics_.add_error(diag::System::Writer,
+                                   "Type f16 is not completely implemented yet");
+            return false;
+        },
         [&](const sem::F32*) {
             out << "float";
             return true;
diff --git a/src/tint/writer/msl/generator_impl_binary_test.cc b/src/tint/writer/msl/generator_impl_binary_test.cc
index 5262b97..02daae2 100644
--- a/src/tint/writer/msl/generator_impl_binary_test.cc
+++ b/src/tint/writer/msl/generator_impl_binary_test.cc
@@ -171,5 +171,31 @@
     EXPECT_EQ(out.str(), "fmod(left, right)");
 }
 
+TEST_F(MslBinaryTest, BoolAnd) {
+    auto* left = Var("left", nullptr, Expr(true));
+    auto* right = Var("right", nullptr, Expr(false));
+    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kAnd, Expr(left), Expr(right));
+    WrapInFunction(left, right, expr);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_EQ(out.str(), "bool(left & right)");
+}
+
+TEST_F(MslBinaryTest, BoolOr) {
+    auto* left = Var("left", nullptr, Expr(true));
+    auto* right = Var("right", nullptr, Expr(false));
+    auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kOr, Expr(left), Expr(right));
+    WrapInFunction(left, right, expr);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
+    EXPECT_EQ(out.str(), "bool(left | right)");
+}
+
 }  // namespace
 }  // namespace tint::writer::msl
diff --git a/src/tint/writer/msl/generator_impl_builtin_test.cc b/src/tint/writer/msl/generator_impl_builtin_test.cc
index 752fc73..0c1f2a4 100644
--- a/src/tint/writer/msl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/msl/generator_impl_builtin_test.cc
@@ -189,7 +189,7 @@
 
     GeneratorImpl& gen = Build();
 
-    auto* sem = program->Sem().Get(call);
+    auto* sem = program->Sem().Get<sem::Call>(call);
     ASSERT_NE(sem, nullptr);
     auto* target = sem->Target();
     ASSERT_NE(target, nullptr);
diff --git a/src/tint/writer/msl/generator_impl_import_test.cc b/src/tint/writer/msl/generator_impl_import_test.cc
index 8a6d2d0..de9353e 100644
--- a/src/tint/writer/msl/generator_impl_import_test.cc
+++ b/src/tint/writer/msl/generator_impl_import_test.cc
@@ -40,7 +40,7 @@
 
     GeneratorImpl& gen = Build();
 
-    auto* sem = program->Sem().Get(call);
+    auto* sem = program->Sem().Get<sem::Call>(call);
     ASSERT_NE(sem, nullptr);
     auto* target = sem->Target();
     ASSERT_NE(target, nullptr);
diff --git a/src/tint/writer/msl/generator_impl_test.cc b/src/tint/writer/msl/generator_impl_test.cc
index b5af8e4..dd90715 100644
--- a/src/tint/writer/msl/generator_impl_test.cc
+++ b/src/tint/writer/msl/generator_impl_test.cc
@@ -22,6 +22,15 @@
 
 using MslGeneratorImplTest = TestHelper;
 
+TEST_F(MslGeneratorImplTest, InvalidProgram) {
+    Diagnostics().add_error(diag::System::Writer, "make the program invalid");
+    ASSERT_FALSE(IsValid());
+    auto program = std::make_unique<Program>(std::move(*this));
+    ASSERT_FALSE(program->IsValid());
+    auto result = Generate(program.get(), Options{});
+    EXPECT_EQ(result.error, "input program is not valid");
+}
+
 TEST_F(MslGeneratorImplTest, Generate) {
     Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{},
          ast::AttributeList{
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 7d232c0..5f8072b 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -30,6 +30,7 @@
 #include "src/tint/sem/depth_multisampled_texture.h"
 #include "src/tint/sem/depth_texture.h"
 #include "src/tint/sem/function.h"
+#include "src/tint/sem/materialize.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/module.h"
 #include "src/tint/sem/multisampled_texture.h"
@@ -55,8 +56,9 @@
 
 uint32_t size_of(const InstructionList& instructions) {
     uint32_t size = 0;
-    for (const auto& inst : instructions)
+    for (const auto& inst : instructions) {
         size += inst.word_length();
+    }
 
     return size;
 }
@@ -256,7 +258,7 @@
     push_memory_model(spv::Op::OpMemoryModel,
                       {U32Operand(SpvAddressingModelLogical), U32Operand(SpvMemoryModelGLSL450)});
 
-    for (auto ext : builder_.AST().Extensions()) {
+    for (auto ext : builder_.Sem().Module()->Extensions()) {
         GenerateExtension(ext);
     }
 
@@ -366,7 +368,7 @@
     }
 }
 
-bool Builder::GenerateExtension(ast::Enable::ExtensionKind) {
+bool Builder::GenerateExtension(ast::Extension) {
     /*
     For each supported extension, push corresponding capability into the builder.
     For example:
@@ -1272,7 +1274,13 @@
             return ast::TraverseAction::Descend;
         }
         if (auto* ce = e->As<ast::CallExpression>()) {
-            auto* call = builder_.Sem().Get(ce);
+            auto* sem = builder_.Sem().Get(ce);
+            if (sem->Is<sem::Materialize>()) {
+                // Materialize can only occur on compile time expressions, so this sub-tree must be
+                // constant.
+                return ast::TraverseAction::Skip;
+            }
+            auto* call = sem->As<sem::Call>();
             if (call->Target()->Is<sem::TypeConstructor>()) {
                 return ast::TraverseAction::Descend;
             }
@@ -1628,6 +1636,8 @@
                     constant.kind = ScalarConstant::Kind::kF32;
                     constant.value.f32 = static_cast<float>(f->value);
                     return;
+                case ast::FloatLiteralExpression::Suffix::kH:
+                    error_ = "Type f16 is not completely implemented yet";
             }
         },
         [&](Default) { error_ = "unknown literal type"; });
@@ -2151,7 +2161,12 @@
 }
 
 uint32_t Builder::GenerateCallExpression(const ast::CallExpression* expr) {
-    auto* call = builder_.Sem().Get(expr);
+    auto* sem = builder_.Sem().Get(expr);
+    if (auto* m = sem->As<sem::Materialize>()) {
+        // TODO(crbug.com/tint/1504): Just emit the constant value.
+        sem = m->Expr();
+    }
+    auto* call = sem->As<sem::Call>();
     auto* target = call->Target();
     return Switch(
         target, [&](const sem::Function* func) { return GenerateFunctionCall(call, func); },
@@ -3672,6 +3687,11 @@
                 push_type(spv::Op::OpTypeFloat, {result, Operand(32u)});
                 return true;
             },
+            [&](const sem::F16*) {
+                // Should be `push_type(spv::Op::OpTypeFloat, {result, Operand(16u)});`
+                error_ = "Type f16 is not completely implemented yet.";
+                return false;
+            },
             [&](const sem::I32*) {
                 push_type(spv::Op::OpTypeInt, {result, Operand(32u), Operand(1u)});
                 return true;
diff --git a/src/tint/writer/spirv/builder.h b/src/tint/writer/spirv/builder.h
index 34cfd76..1745ed5 100644
--- a/src/tint/writer/spirv/builder.h
+++ b/src/tint/writer/spirv/builder.h
@@ -224,11 +224,11 @@
                                      ast::InterpolationType type,
                                      ast::InterpolationSampling sampling);
 
-    /// Generates a extension for the given extension kind. Emits an error and
-    /// returns false if the extension kind is not supported.
-    /// @param kind ExtensionKind of the extension to generate
+    /// Generates the enabling of an extension. Emits an error and returns false if the extension is
+    /// not supported.
+    /// @param ext the extension to generate
     /// @returns true on success.
-    bool GenerateExtension(ast::Enable::ExtensionKind kind);
+    bool GenerateExtension(ast::Extension ext);
     /// Generates a label for the given id. Emits an error and returns false if
     /// we're currently outside a function.
     /// @param id the id to use for the label
diff --git a/src/tint/writer/spirv/builder_test.cc b/src/tint/writer/spirv/builder_test.cc
index f2475c0..3548f9a 100644
--- a/src/tint/writer/spirv/builder_test.cc
+++ b/src/tint/writer/spirv/builder_test.cc
@@ -20,6 +20,15 @@
 
 using BuilderTest = TestHelper;
 
+TEST_F(BuilderTest, InvalidProgram) {
+    Diagnostics().add_error(diag::System::Writer, "make the program invalid");
+    ASSERT_FALSE(IsValid());
+    auto program = std::make_unique<Program>(std::move(*this));
+    ASSERT_FALSE(program->IsValid());
+    auto result = Generate(program.get(), Options{});
+    EXPECT_EQ(result.error, "input program is not valid");
+}
+
 TEST_F(BuilderTest, TracksIdBounds) {
     spirv::Builder& b = Build();
 
diff --git a/src/tint/writer/spirv/generator.cc b/src/tint/writer/spirv/generator.cc
index fa67827..93ac6e1 100644
--- a/src/tint/writer/spirv/generator.cc
+++ b/src/tint/writer/spirv/generator.cc
@@ -26,6 +26,10 @@
 
 Result Generate(const Program* program, const Options& options) {
     Result result;
+    if (!program->IsValid()) {
+        result.error = "input program is not valid";
+        return result;
+    }
 
     // Sanitize the program.
     auto sanitized_result = Sanitize(program, options);
diff --git a/src/tint/writer/spirv/generator_impl.cc b/src/tint/writer/spirv/generator_impl.cc
index e466a24..21216bf 100644
--- a/src/tint/writer/spirv/generator_impl.cc
+++ b/src/tint/writer/spirv/generator_impl.cc
@@ -21,6 +21,7 @@
 #include "src/tint/transform/add_spirv_block_attribute.h"
 #include "src/tint/transform/builtin_polyfill.h"
 #include "src/tint/transform/canonicalize_entry_point_io.h"
+#include "src/tint/transform/disable_uniformity_analysis.h"
 #include "src/tint/transform/expand_compound_assignment.h"
 #include "src/tint/transform/fold_constants.h"
 #include "src/tint/transform/for_loop_to_loop.h"
@@ -41,6 +42,8 @@
     transform::Manager manager;
     transform::DataMap data;
 
+    manager.Add<transform::DisableUniformityAnalysis>();
+
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.count_leading_zeros = true;
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 3ef6269..677421d 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -62,12 +62,12 @@
 
 bool GeneratorImpl::Generate() {
     // Generate enable directives before any other global declarations.
-    for (auto ext : program_->AST().Extensions()) {
-        if (!EmitEnableDirective(ext)) {
+    for (auto enable : program_->AST().Enables()) {
+        if (!EmitEnable(enable)) {
             return false;
         }
     }
-    if (!program_->AST().Extensions().empty()) {
+    if (!program_->AST().Enables().empty()) {
         line();
     }
     // Generate global declarations in the order they appear in the module.
@@ -94,13 +94,9 @@
     return true;
 }
 
-bool GeneratorImpl::EmitEnableDirective(const ast::Enable::ExtensionKind ext) {
+bool GeneratorImpl::EmitEnable(const ast::Enable* enable) {
     auto out = line();
-    auto extension = ast::Enable::KindToName(ext);
-    if (extension == "") {
-        return false;
-    }
-    out << "enable " << extension << ";";
+    out << "enable " << enable->extension << ";";
     return true;
 }
 
@@ -405,6 +401,11 @@
             out << "f32";
             return true;
         },
+        [&](const ast::F16*) {
+            diagnostics_.add_error(diag::System::Writer,
+                                   "Type f16 is not completely implemented yet.");
+            return false;
+        },
         [&](const ast::I32*) {
             out << "i32";
             return true;
diff --git a/src/tint/writer/wgsl/generator_impl.h b/src/tint/writer/wgsl/generator_impl.h
index 8473b4f..a17e2da 100644
--- a/src/tint/writer/wgsl/generator_impl.h
+++ b/src/tint/writer/wgsl/generator_impl.h
@@ -53,9 +53,9 @@
     bool Generate();
 
     /// Handles generating a enable directive
-    /// @param ext the extension kind in the enable directive to generate
+    /// @param enable the enable node
     /// @returns true if the enable directive was emitted
-    bool EmitEnableDirective(const ast::Enable::ExtensionKind ext);
+    bool EmitEnable(const ast::Enable* enable);
     /// Handles generating a declared type
     /// @param ty the declared type to generate
     /// @returns true if the declared type was emitted
diff --git a/src/tint/writer/wgsl/generator_impl_enable_test.cc b/src/tint/writer/wgsl/generator_impl_enable_test.cc
index f9de371..503a9a0 100644
--- a/src/tint/writer/wgsl/generator_impl_enable_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_enable_test.cc
@@ -20,10 +20,12 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Enable) {
+    auto* enable = Enable(ast::Extension::kF16);
+
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitEnableDirective(ast::Enable::ExtensionKind::kInternalExtensionForTesting));
-    EXPECT_EQ(gen.result(), R"(enable InternalExtensionForTesting;
+    ASSERT_TRUE(gen.EmitEnable(enable));
+    EXPECT_EQ(gen.result(), R"(enable f16;
 )");
 }
 
diff --git a/test/tint/BUILD.gn b/test/tint/BUILD.gn
index bed8729..2ef1202 100644
--- a/test/tint/BUILD.gn
+++ b/test/tint/BUILD.gn
@@ -164,7 +164,9 @@
     "../../src/tint/ast/depth_texture_test.cc",
     "../../src/tint/ast/discard_statement_test.cc",
     "../../src/tint/ast/enable_test.cc",
+    "../../src/tint/ast/extension_test.cc",
     "../../src/tint/ast/external_texture_test.cc",
+    "../../src/tint/ast/f16_test.cc",
     "../../src/tint/ast/f32_test.cc",
     "../../src/tint/ast/fallthrough_statement_test.cc",
     "../../src/tint/ast/float_literal_expression_test.cc",
@@ -272,6 +274,7 @@
     "../../src/tint/resolver/struct_storage_class_use_test.cc",
     "../../src/tint/resolver/type_constructor_validation_test.cc",
     "../../src/tint/resolver/type_validation_test.cc",
+    "../../src/tint/resolver/uniformity_test.cc",
     "../../src/tint/resolver/validation_test.cc",
     "../../src/tint/resolver/validator_is_storeable_test.cc",
     "../../src/tint/resolver/var_let_test.cc",
@@ -287,7 +290,9 @@
     "../../src/tint/sem/builtin_test.cc",
     "../../src/tint/sem/depth_multisampled_texture_test.cc",
     "../../src/tint/sem/depth_texture_test.cc",
+    "../../src/tint/sem/expression_test.cc",
     "../../src/tint/sem/external_texture_test.cc",
+    "../../src/tint/sem/f16_test.cc",
     "../../src/tint/sem/f32_test.cc",
     "../../src/tint/sem/i32_test.cc",
     "../../src/tint/sem/matrix_test.cc",
@@ -300,6 +305,7 @@
     "../../src/tint/sem/sem_struct_test.cc",
     "../../src/tint/sem/storage_texture_test.cc",
     "../../src/tint/sem/texture_test.cc",
+    "../../src/tint/sem/type_test.cc",
     "../../src/tint/sem/type_manager_test.cc",
     "../../src/tint/sem/u32_test.cc",
     "../../src/tint/sem/vector_test.cc",
@@ -323,6 +329,7 @@
     "../../src/tint/transform/decompose_memory_access_test.cc",
     "../../src/tint/transform/decompose_strided_array_test.cc",
     "../../src/tint/transform/decompose_strided_matrix_test.cc",
+    "../../src/tint/transform/disable_uniformity_analysis_test.cc",
     "../../src/tint/transform/expand_compound_assignment_test.cc",
     "../../src/tint/transform/first_index_offset_test.cc",
     "../../src/tint/transform/fold_constants_test.cc",