Import Tint changes from Dawn

Changes:
  - ce93a6b224f7854c52c617197cfc317fb7fb949a tint: Replace type::UniqueNode::Hash() virtual with field by Ben Clayton <bclayton@google.com>
  - 0fcc00e22a1744580560b8c3c7f75c6e0d7002ca tint/uniformity: Fix debug graph output by James Price <jrprice@google.com>
  - 2f9a98870eae19d1a69d67d8c74ce69f727f5c3d tint: Implement sem::Load by Ben Clayton <bclayton@google.com>
  - bf8a230c81ea6490ff67b8887c38ce5df3ccd5f9 tint: const eval of ldexp builtin by Antonio Maiorano <amaiorano@google.com>
  - 7c3e9a6dd25fa98077d9ae7d6d815dde45ca86c6 tint: Rework tint::Manager by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: ce93a6b224f7854c52c617197cfc317fb7fb949a
Change-Id: I78b9226c4441602f7ce189c6c8f8bf4f4dab8fdb
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/114863
Reviewed-by: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 87eb852..acb05e0 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -583,6 +583,7 @@
     "type/texture.h",
     "type/type.h",
     "type/u32.h",
+    "type/unique_node.h",
     "type/vector.h",
     "type/void.h",
     "utils/bitcast.h",
@@ -754,6 +755,8 @@
     "type/type.h",
     "type/u32.cc",
     "type/u32.h",
+    "type/unique_node.cc",
+    "type/unique_node.h",
     "type/vector.cc",
     "type/vector.h",
     "type/void.cc",
@@ -1202,6 +1205,7 @@
       "resolver/intrinsic_table_test.cc",
       "resolver/is_host_shareable_test.cc",
       "resolver/is_storeable_test.cc",
+      "resolver/load_test.cc",
       "resolver/materialize_test.cc",
       "resolver/override_test.cc",
       "resolver/ptr_ref_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index ee350f1..0777746 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -514,6 +514,8 @@
   type/type.h
   type/u32.cc
   type/u32.h
+  type/unique_node.cc
+  type/unique_node.h
   type/vector.cc
   type/vector.h
   type/void.cc
@@ -911,6 +913,7 @@
     resolver/intrinsic_table_test.cc
     resolver/is_host_shareable_test.cc
     resolver/is_storeable_test.cc
+    resolver/load_test.cc
     resolver/materialize_test.cc
     resolver/override_test.cc
     resolver/ptr_ref_test.cc
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 2f6467e..2bb3fe7 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -226,6 +226,7 @@
 match fa_f32: fa | f32
 match fa_f32_f16: fa | f32 | f16
 match ia_iu32: ia | i32 | u32
+match ia_i32: ia | i32
 match fiu32_f16: f32 | i32 | u32 | f16
 match fiu32: f32 | i32 | u32
 match fi32_f16: f32 | i32 | f16
@@ -527,8 +528,8 @@
 @const fn insertBits<N: num, T: iu32>(vec<N, T>, vec<N, T>, u32, u32) -> vec<N, T>
 @const fn inverseSqrt<T: fa_f32_f16>(T) -> T
 @const fn inverseSqrt<N: num, T: fa_f32_f16>(vec<N, T>) -> vec<N, T>
-fn ldexp<T: f32_f16>(T, i32) -> T
-fn ldexp<N: num, T: f32_f16>(vec<N, T>, vec<N, i32>) -> vec<N, T>
+@const fn ldexp<T: fa_f32_f16, U: ia_i32>(T, U) -> T
+@const fn ldexp<N: num, T: fa_f32_f16, U: ia_i32>(vec<N, T>, vec<N, U>) -> vec<N, T>
 @const fn length<T: fa_f32_f16>(@test_value(0.0) T) -> T
 @const fn length<N: num, T: fa_f32_f16>(@test_value(0.0) vec<N, T>) -> T
 @const fn log<T: fa_f32_f16>(T) -> T
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 597463c..5396cdd 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -539,35 +539,18 @@
         return constant_nodes_.Create<constant::Splat>(type, element, n);
     }
 
-    /// Creates a new type::Type owned by the ProgramBuilder.
-    /// When the ProgramBuilder is destructed, owned ProgramBuilder and the
-    /// returned `Type` will also be destructed.
-    /// Types are unique (de-aliased), and so calling create() for the same `T`
-    /// and arguments will return the same pointer.
-    /// @param args the arguments to pass to the type constructor
-    /// @returns the de-aliased type pointer
+    /// Creates a new type::Node owned by the ProgramBuilder.
+    /// When the ProgramBuilder is destructed, owned ProgramBuilder and the returned node will also
+    /// be destructed. If T derives from type::UniqueNode, then the calling create() for the same
+    /// `T` and arguments will return the same pointer.
+    /// @param args the arguments to pass to the constructor
+    /// @returns the new, or existing node
     template <typename T, typename... ARGS>
-    traits::EnableIfIsType<T, type::Type>* create(ARGS&&... args) {
+    traits::EnableIfIsType<T, type::Node>* create(ARGS&&... args) {
         AssertNotMoved();
         return types_.Get<T>(std::forward<ARGS>(args)...);
     }
 
-    /// Creates a new type::ArrayCount owned by the ProgramBuilder.
-    /// When the ProgramBuilder is destructed, owned ProgramBuilder and the
-    /// returned `ArrayCount` will also be destructed.
-    /// ArrayCounts are unique (de-aliased), and so calling create() for the same `T`
-    /// and arguments will return the same pointer.
-    /// @param args the arguments to pass to the array count constructor
-    /// @returns the de-aliased array count pointer
-    template <typename T, typename... ARGS>
-    traits::EnableIf<traits::IsTypeOrDerived<T, type::ArrayCount> ||
-                         traits::IsTypeOrDerived<T, type::StructMember>,
-                     T>*
-    create(ARGS&&... args) {
-        AssertNotMoved();
-        return types_.GetNode<T>(std::forward<ARGS>(args)...);
-    }
-
     /// Marks this builder as moved, preventing any further use of the builder.
     void MarkAsMoved();
 
diff --git a/src/tint/resolver/alias_analysis_test.cc b/src/tint/resolver/alias_analysis_test.cc
index 38710b6..490f54c 100644
--- a/src/tint/resolver/alias_analysis_test.cc
+++ b/src/tint/resolver/alias_analysis_test.cc
@@ -804,6 +804,37 @@
 12:34 note: aliases with another argument passed here)");
 }
 
+TEST_F(ResolverAliasAnalysisTest, Read_MultiComponentSwizzle) {
+    // fn f2(p1 : ptr<function, vec4<f32>, p2 : ptr<function, vec4<f32>) {
+    //   _ = (*p2).zy;
+    //   *p1 = vec4<f32>();
+    // }
+    // fn f1() {
+    //   var v : vec4<f32>;
+    //   f2(&v, &v);
+    // }
+    Structure("S", utils::Vector{Member("a", ty.i32())});
+    Func("f2",
+         utils::Vector{
+             Param("p1", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction)),
+             Param("p2", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             Assign(Phony(), MemberAccessor(Deref("p2"), "zy")),
+             Assign(Deref("p1"), Construct(ty.vec4<f32>())),
+         });
+    Func("f1", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Var("v", ty.vec4<f32>())),
+             CallStmt(
+                 Call("f2", AddressOf(Source{{12, 34}}, "v"), AddressOf(Source{{56, 76}}, "v"))),
+         });
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), R"(56:76 warning: invalid aliased pointer argument
+12:34 note: aliases with another argument passed here)");
+}
+
 TEST_F(ResolverAliasAnalysisTest, SinglePointerReadWrite) {
     // Test that we can both read and write from a single pointer parameter.
     //
diff --git a/src/tint/resolver/array_accessor_test.cc b/src/tint/resolver/array_accessor_test.cc
index 9c46789..65fbb83 100644
--- a/src/tint/resolver/array_accessor_test.cc
+++ b/src/tint/resolver/array_accessor_test.cc
@@ -43,7 +43,7 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -58,7 +58,7 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -73,7 +73,7 @@
     EXPECT_TRUE(r()->Resolve());
     EXPECT_EQ(r()->error(), "");
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -108,13 +108,10 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     ASSERT_NE(TypeOf(acc), nullptr);
-    ASSERT_TRUE(TypeOf(acc)->Is<type::Reference>());
+    ASSERT_TRUE(TypeOf(acc)->Is<type::Vector>());
+    EXPECT_EQ(TypeOf(acc)->As<type::Vector>()->Width(), 3u);
 
-    auto* ref = TypeOf(acc)->As<type::Reference>();
-    ASSERT_TRUE(ref->StoreType()->Is<type::Vector>());
-    EXPECT_EQ(ref->StoreType()->As<type::Vector>()->Width(), 3u);
-
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -129,12 +126,9 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     ASSERT_NE(TypeOf(acc), nullptr);
-    ASSERT_TRUE(TypeOf(acc)->Is<type::Reference>());
+    EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
 
-    auto* ref = TypeOf(acc)->As<type::Reference>();
-    EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
-
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -157,7 +151,7 @@
 
     EXPECT_TRUE(r()->Resolve());
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -181,12 +175,9 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     ASSERT_NE(TypeOf(acc), nullptr);
-    ASSERT_TRUE(TypeOf(acc)->Is<type::Reference>());
+    EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
 
-    auto* ref = TypeOf(acc)->As<type::Reference>();
-    EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
-
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -197,12 +188,9 @@
     auto* acc = IndexAccessor("my_var", 2_i);
     WrapInFunction(acc);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
-    ASSERT_NE(TypeOf(acc), nullptr);
-    auto* ref = TypeOf(acc)->As<type::Reference>();
-    ASSERT_NE(ref, nullptr);
-    EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
+    EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -213,12 +201,9 @@
     auto* acc = IndexAccessor("my_var", 2_u);
     WrapInFunction(acc);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
-    ASSERT_NE(TypeOf(acc), nullptr);
-    auto* ref = TypeOf(acc)->As<type::Reference>();
-    ASSERT_NE(ref, nullptr);
-    EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
+    EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -229,12 +214,9 @@
     auto* acc = IndexAccessor("my_var", 2_a);
     WrapInFunction(acc);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
-    ASSERT_NE(TypeOf(acc), nullptr);
-    auto* ref = TypeOf(acc)->As<type::Reference>();
-    ASSERT_NE(ref, nullptr);
-    EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
+    EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -251,12 +233,9 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     ASSERT_NE(TypeOf(acc), nullptr);
-    ASSERT_TRUE(TypeOf(acc)->Is<type::Reference>());
+    EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
 
-    auto* ref = TypeOf(acc)->As<type::Reference>();
-    EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
-
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -292,7 +271,7 @@
     EXPECT_TRUE(r()->Resolve());
     EXPECT_EQ(r()->error(), "");
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -325,7 +304,7 @@
          });
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -346,7 +325,7 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
+    auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
     ASSERT_NE(idx_sem, nullptr);
     EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
     EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index 7340387..6bda08a 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -181,7 +181,7 @@
     WrapInFunction(Decl(Var("v", use)));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
-    auto* sem = Sem().Get<sem::VariableUser>(use);
+    auto* sem = Sem().Get(use)->UnwrapLoad()->As<sem::VariableUser>();
     ASSERT_NE(sem, nullptr);
     EXPECT_EQ(sem->Variable(), Sem().Get(mix));
 }
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index 628975b..29c57c7 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -2654,6 +2654,48 @@
     return TransformElements(builder, ty, transform, args[0]);
 }
 
+ConstEval::Result ConstEval::ldexp(const type::Type* ty,
+                                   utils::VectorRef<const constant::Value*> args,
+                                   const Source& source) {
+    auto transform = [&](const constant::Value* c1, size_t index) {
+        auto create = [&](auto e1) -> ConstEval::Result {
+            using E1Type = decltype(e1);
+            // If e1 is AFloat, then e2 is AInt, otherwise it's i32
+            using E2Type = std::conditional_t<std::is_same_v<E1Type, AFloat>, AInt, i32>;
+
+            E2Type e2;
+            auto* c2 = args[1];
+            if (c2->Type()->Is<type::Vector>()) {
+                e2 = c2->Index(index)->ValueAs<E2Type>();
+            } else {
+                e2 = c2->ValueAs<E2Type>();
+            }
+
+            E2Type bias;
+            if constexpr (std::is_same_v<E1Type, f16>) {
+                bias = 15;
+            } else if constexpr (std::is_same_v<E1Type, f32>) {
+                bias = 127;
+            } else {
+                bias = 1023;
+            }
+
+            if (e2 > bias + 1) {
+                AddError("e2 must be less than or equal to " + std::to_string(bias + 1), source);
+                return utils::Failure;
+            }
+
+            auto target_ty = type::Type::DeepestElementOf(ty);
+
+            auto r = std::ldexp(e1, static_cast<int>(e2));
+            return CreateScalar(builder, source, target_ty, E1Type{r});
+        };
+        return Dispatch_fa_f32_f16(create, c1);
+    };
+
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
 ConstEval::Result ConstEval::length(const type::Type* ty,
                                     utils::VectorRef<const constant::Value*> args,
                                     const Source& source) {
diff --git a/src/tint/resolver/const_eval.h b/src/tint/resolver/const_eval.h
index baeb7e4..d75f5de 100644
--- a/src/tint/resolver/const_eval.h
+++ b/src/tint/resolver/const_eval.h
@@ -725,6 +725,15 @@
                        utils::VectorRef<const constant::Value*> args,
                        const Source& source);
 
+    /// ldexp builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location
+    /// @return the result value, or null if the value cannot be calculated
+    Result ldexp(const type::Type* ty,
+                 utils::VectorRef<const constant::Value*> args,
+                 const Source& source);
+
     /// length builtin
     /// @param ty the expression type
     /// @param args the input arguments
diff --git a/src/tint/resolver/const_eval_builtin_test.cc b/src/tint/resolver/const_eval_builtin_test.cc
index 10070db..76f8238 100644
--- a/src/tint/resolver/const_eval_builtin_test.cc
+++ b/src/tint/resolver/const_eval_builtin_test.cc
@@ -1551,6 +1551,71 @@
                                               ExtractBitsCases<u32>()))));
 
 template <typename T>
+std::vector<Case> LdexpCases() {
+    using T2 = std::conditional_t<std::is_same_v<T, AFloat>, AInt, i32>;
+    T2 bias;
+    if constexpr (std::is_same_v<T, f16>) {
+        bias = 15;
+    } else if constexpr (std::is_same_v<T, f32>) {
+        bias = 127;
+    } else {
+        bias = 1023;
+    }
+
+    auto compute = [](T e1, T2 e2) { return T{std::ldexp(e1.value, static_cast<int>(e2.value))}; };
+
+    auto r = std::vector<Case>{
+        C({T(0), T2(0)}, T(0)),          //
+        C({T(7), T2(4)}, T(112)),        //
+        C({T(7), T2(5)}, T(224)),        //
+        C({T(7), T2(6)}, T(448)),        //
+        C({T(7), T2(-4)}, T(0.4375)),    //
+        C({T(7), T2(-5)}, T(0.21875)),   //
+        C({T(7), T2(-6)}, T(0.109375)),  //
+        // With bias exponent
+        C({T(0), T2(bias)}, T(0)),                           //
+        C({T(0), T2(bias + 1)}, T(0)),                       //
+        C({T(1), T2(bias)}, compute(T(1), T2(bias))),        //
+        C({T(0.5), T2(bias)}, compute(T(0.5), T2(bias))),    //
+        C({T(0.25), T2(bias)}, compute(T(0.25), T2(bias))),  //
+        // The result may be zero if e2 + bias ≤ 0.
+        C({T(0), T2(-bias)}, T(0)),      //
+        C({T(0), T2(-bias - 1)}, T(0)),  //
+        C({T(0), T2(-bias - 2)}, T(0)),  //
+
+        // Vector tests
+        C({Vec(T(0), T(7), T(7)), Vec(T2(0), T2(4), T2(-4))}, Vec(T(0), T(112), T(0.4375))),
+    };
+
+    std::string e2_too_large_error_msg =
+        "12:34 error: e2 must be less than or equal to " + std::to_string(bias + 1);
+    auto val_overflow_error_msg = [](auto val) {
+        return "12:34 error: " + OverflowErrorMessage(val, FriendlyName<T>());
+    };
+    ConcatInto(r, std::vector<Case>{
+                      // e2 is > bias + 1
+                      E({T(0), T2(bias + 2)}, e2_too_large_error_msg),
+                      E({T(0), T2(bias + 1000)}, e2_too_large_error_msg),
+                      E({T(0), T2::Highest()}, e2_too_large_error_msg),
+                      // Result is inf
+                      E({T(1), T2(bias + 1)}, val_overflow_error_msg(T::Inf())),
+                      E({T(2), T2(bias + 1)}, val_overflow_error_msg(T::Inf())),
+                      E({T::Highest(), T2(bias + 1)}, val_overflow_error_msg(T::Inf())),
+                      E({T(-1), T2(bias + 1)}, val_overflow_error_msg(-T::Inf())),
+                      E({T(-2), T2(bias + 1)}, val_overflow_error_msg(-T::Inf())),
+                      E({T::Lowest(), T2(bias + 1)}, val_overflow_error_msg(-T::Inf())),
+                  });
+    return r;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Ldexp,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kLdexp),
+                     testing::ValuesIn(Concat(LdexpCases<AFloat>(),  //
+                                              LdexpCases<f32>(),
+                                              LdexpCases<f16>()))));
+
+template <typename T>
 std::vector<Case> LengthCases() {
     const auto kSqrtOfHighest = T(std::sqrt(T::Highest()));
     const auto kSqrtOfHighestSquared = T(kSqrtOfHighest * kSqrtOfHighest);
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index e6fd3df..54c851d 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -57,12 +57,11 @@
 /// A special type that matches all TypeMatchers
 class Any final : public Castable<Any, type::Type> {
   public:
-    Any() : Base(type::Flags{}) {}
+    Any() : Base(0u, type::Flags{}) {}
     ~Any() override = default;
 
     // Stub implementations for type::Type conformance.
-    size_t Hash() const override { return 0; }
-    bool Equals(const type::Type&) const override { return false; }
+    bool Equals(const type::UniqueNode&) const override { return false; }
     std::string FriendlyName(const SymbolTable&) const override { return "<any>"; }
 };
 
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index 926f6b5..0507d94 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -2112,6 +2112,40 @@
   return ss.str();
 }
 
+/// TypeMatcher for 'match ia_i32'
+class IaI32 : 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 type::Type* Match(MatchState& state,
+                         const type::Type* type) const override;
+  /// @param state the MatchState
+  /// @return a string representation of the matcher.
+  std::string String(MatchState* state) const override;
+};
+
+const type::Type* IaI32::Match(MatchState& state, const type::Type* ty) const {
+  if (match_ia(state, ty)) {
+    return build_ia(state);
+  }
+  if (match_i32(state, ty)) {
+    return build_i32(state);
+  }
+  return nullptr;
+}
+
+std::string IaI32::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 << Ia().String(nullptr) << " or " << I32().String(nullptr);
+  return ss.str();
+}
+
 /// TypeMatcher for 'match fiu32_f16'
 class Fiu32F16 : public TypeMatcher {
  public:
@@ -2625,6 +2659,7 @@
   FaF32 FaF32_;
   FaF32F16 FaF32F16_;
   IaIu32 IaIu32_;
+  IaI32 IaI32_;
   Fiu32F16 Fiu32F16_;
   Fiu32 Fiu32_;
   Fi32F16 Fi32F16_;
@@ -2647,7 +2682,7 @@
   ~Matchers();
 
   /// The template types, types, and type matchers
-  TypeMatcher const* const type[71] = {
+  TypeMatcher const* const type[72] = {
     /* [0] */ &template_type_0_,
     /* [1] */ &template_type_1_,
     /* [2] */ &template_type_2_,
@@ -2713,12 +2748,13 @@
     /* [62] */ &FaF32_,
     /* [63] */ &FaF32F16_,
     /* [64] */ &IaIu32_,
-    /* [65] */ &Fiu32F16_,
-    /* [66] */ &Fiu32_,
-    /* [67] */ &Fi32F16_,
-    /* [68] */ &Fi32_,
-    /* [69] */ &F32F16_,
-    /* [70] */ &Iu32_,
+    /* [65] */ &IaI32_,
+    /* [66] */ &Fiu32F16_,
+    /* [67] */ &Fiu32_,
+    /* [68] */ &Fi32F16_,
+    /* [69] */ &Fi32_,
+    /* [70] */ &F32F16_,
+    /* [71] */ &Iu32_,
   };
 
   /// The template numbers, and number matchers
@@ -2785,7 +2821,7 @@
   /* [41] */ 0,
   /* [42] */ 23,
   /* [43] */ 0,
-  /* [44] */ 7,
+  /* [44] */ 1,
   /* [45] */ 48,
   /* [46] */ 0,
   /* [47] */ 0,
@@ -6046,7 +6082,7 @@
   {
     /* [611] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[9],
+    /* matcher indices */ &kMatcherIndices[16],
   },
   {
     /* [612] */
@@ -8054,137 +8090,137 @@
   {
     /* [0] */
     /* name */ "T",
-    /* matcher index */ 66,
+    /* matcher index */ 67,
   },
   {
     /* [1] */
     /* name */ "C",
-    /* matcher index */ 70,
+    /* matcher index */ 71,
   },
   {
     /* [2] */
     /* name */ "A",
-    /* matcher index */ 70,
+    /* matcher index */ 71,
   },
   {
     /* [3] */
     /* name */ "L",
-    /* matcher index */ 70,
+    /* matcher index */ 71,
   },
   {
     /* [4] */
     /* name */ "T",
-    /* matcher index */ 66,
+    /* matcher index */ 67,
   },
   {
     /* [5] */
     /* name */ "C",
-    /* matcher index */ 70,
+    /* matcher index */ 71,
   },
   {
     /* [6] */
     /* name */ "L",
-    /* matcher index */ 70,
+    /* matcher index */ 71,
   },
   {
     /* [7] */
     /* name */ "T",
-    /* matcher index */ 66,
+    /* matcher index */ 67,
   },
   {
     /* [8] */
     /* name */ "C",
-    /* matcher index */ 70,
+    /* matcher index */ 71,
   },
   {
     /* [9] */
     /* name */ "S",
-    /* matcher index */ 70,
+    /* matcher index */ 71,
   },
   {
     /* [10] */
     /* name */ "T",
-    /* matcher index */ 66,
+    /* matcher index */ 63,
   },
   {
     /* [11] */
-    /* name */ "L",
-    /* matcher index */ 70,
+    /* name */ "U",
+    /* matcher index */ 65,
   },
   {
     /* [12] */
     /* name */ "T",
-    /* matcher index */ 9,
+    /* matcher index */ 67,
   },
   {
     /* [13] */
-    /* name */ "U",
-    /* matcher index */ 54,
+    /* name */ "L",
+    /* matcher index */ 71,
   },
   {
     /* [14] */
     /* name */ "T",
-    /* matcher index */ 10,
+    /* matcher index */ 9,
   },
   {
     /* [15] */
     /* name */ "U",
-    /* matcher index */ 55,
+    /* matcher index */ 54,
   },
   {
     /* [16] */
     /* name */ "T",
-    /* matcher index */ 7,
+    /* matcher index */ 10,
   },
   {
     /* [17] */
     /* name */ "U",
-    /* matcher index */ 56,
+    /* matcher index */ 55,
   },
   {
     /* [18] */
     /* name */ "T",
-    /* matcher index */ 8,
+    /* matcher index */ 7,
   },
   {
     /* [19] */
     /* name */ "U",
-    /* matcher index */ 57,
+    /* matcher index */ 56,
   },
   {
     /* [20] */
     /* name */ "T",
-    /* matcher index */ 4,
+    /* matcher index */ 8,
   },
   {
     /* [21] */
     /* name */ "U",
-    /* matcher index */ 58,
+    /* matcher index */ 57,
   },
   {
     /* [22] */
     /* name */ "T",
-    /* matcher index */ 59,
+    /* matcher index */ 4,
   },
   {
     /* [23] */
-    /* name */ "T",
-    /* matcher index */ 63,
+    /* name */ "U",
+    /* matcher index */ 58,
   },
   {
     /* [24] */
     /* name */ "T",
-    /* matcher index */ kNoMatcher,
+    /* matcher index */ 59,
   },
   {
     /* [25] */
     /* name */ "T",
-    /* matcher index */ 70,
+    /* matcher index */ kNoMatcher,
   },
   {
     /* [26] */
     /* name */ "T",
-    /* matcher index */ 69,
+    /* matcher index */ 71,
   },
   {
     /* [27] */
@@ -8204,7 +8240,7 @@
   {
     /* [30] */
     /* name */ "T",
-    /* matcher index */ 65,
+    /* matcher index */ 66,
   },
   {
     /* [31] */
@@ -8236,6 +8272,11 @@
     /* name */ "T",
     /* matcher index */ 53,
   },
+  {
+    /* [37] */
+    /* name */ "T",
+    /* matcher index */ 70,
+  },
 };
 
 constexpr TemplateNumberInfo kTemplateNumbers[] = {
@@ -8309,7 +8350,7 @@
     /* num parameters */ 2,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[632],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -8333,7 +8374,7 @@
     /* num parameters */ 2,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[634],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8357,7 +8398,7 @@
     /* num parameters */ 2,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[636],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8381,7 +8422,7 @@
     /* num parameters */ 2,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[638],
     /* return matcher indices */ &kMatcherIndices[122],
@@ -8405,7 +8446,7 @@
     /* num parameters */ 2,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[640],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8429,7 +8470,7 @@
     /* num parameters */ 2,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[642],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8453,7 +8494,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[919],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8477,7 +8518,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[920],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8501,7 +8542,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[921],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8525,7 +8566,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[922],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8549,7 +8590,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[923],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8561,7 +8602,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[924],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -8573,7 +8614,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[925],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8585,7 +8626,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[926],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8597,7 +8638,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[927],
     /* return matcher indices */ &kMatcherIndices[122],
@@ -8609,7 +8650,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[928],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -8621,7 +8662,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[501],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -8633,7 +8674,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[504],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -8645,7 +8686,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[331],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -8681,7 +8722,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[507],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -8693,7 +8734,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[339],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -8705,7 +8746,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[510],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -8729,7 +8770,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[513],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -8741,7 +8782,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[347],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -8777,7 +8818,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[516],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -8921,7 +8962,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[980],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -8933,7 +8974,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[981],
     /* return matcher indices */ &kMatcherIndices[172],
@@ -8945,7 +8986,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[982],
     /* return matcher indices */ &kMatcherIndices[152],
@@ -8957,7 +8998,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[20],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[983],
     /* return matcher indices */ &kMatcherIndices[154],
@@ -8969,7 +9010,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[984],
     /* return matcher indices */ &kMatcherIndices[174],
@@ -8981,7 +9022,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[387],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -8993,7 +9034,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[260],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -9029,7 +9070,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[391],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -9041,7 +9082,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[270],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -9053,7 +9094,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[395],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -9221,7 +9262,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[495],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -9233,7 +9274,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[311],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -9269,7 +9310,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[498],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -9425,7 +9466,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[549],
     /* return matcher indices */ nullptr,
@@ -9509,7 +9550,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[973],
     /* return matcher indices */ &kMatcherIndices[134],
@@ -9521,7 +9562,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[974],
     /* return matcher indices */ &kMatcherIndices[166],
@@ -9533,7 +9574,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[975],
     /* return matcher indices */ &kMatcherIndices[144],
@@ -9545,7 +9586,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[20],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[976],
     /* return matcher indices */ &kMatcherIndices[122],
@@ -9557,7 +9598,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[977],
     /* return matcher indices */ &kMatcherIndices[168],
@@ -9641,7 +9682,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[940],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -9653,7 +9694,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[941],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -9665,7 +9706,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[942],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -9677,7 +9718,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[943],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -9797,7 +9838,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[692],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -9809,7 +9850,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[694],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -9821,7 +9862,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[696],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -9833,7 +9874,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[698],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -9845,7 +9886,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[700],
     /* return matcher indices */ &kMatcherIndices[14],
@@ -9857,7 +9898,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[702],
     /* return matcher indices */ &kMatcherIndices[14],
@@ -9869,7 +9910,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[1],
     /* parameters */ &kParameters[704],
     /* return matcher indices */ &kMatcherIndices[96],
@@ -9881,7 +9922,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[1],
     /* parameters */ &kParameters[706],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -9893,7 +9934,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 3,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[0],
     /* parameters */ &kParameters[708],
     /* return matcher indices */ &kMatcherIndices[26],
@@ -9953,7 +9994,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[966],
     /* return matcher indices */ &kMatcherIndices[108],
@@ -9965,7 +10006,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[967],
     /* return matcher indices */ &kMatcherIndices[162],
@@ -9977,7 +10018,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[968],
     /* return matcher indices */ &kMatcherIndices[132],
@@ -9989,7 +10030,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[20],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[969],
     /* return matcher indices */ &kMatcherIndices[116],
@@ -10001,7 +10042,7 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[970],
     /* return matcher indices */ &kMatcherIndices[164],
@@ -10013,7 +10054,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[359],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10025,7 +10066,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[195],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10061,7 +10102,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[363],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10073,7 +10114,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[205],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10085,7 +10126,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[367],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10109,7 +10150,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[245],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10121,7 +10162,7 @@
     /* num parameters */ 6,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[102],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10157,7 +10198,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[250],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10169,7 +10210,7 @@
     /* num parameters */ 6,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[114],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10181,7 +10222,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[255],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10205,7 +10246,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[323],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10217,7 +10258,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[170],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10253,7 +10294,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[327],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -10277,7 +10318,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[371],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -10289,7 +10330,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[215],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -10325,7 +10366,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[375],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -10349,7 +10390,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[379],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -10361,7 +10402,7 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[230],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -10397,7 +10438,7 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[383],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -10421,7 +10462,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[176],
@@ -10433,7 +10474,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[985],
     /* return matcher indices */ &kMatcherIndices[176],
@@ -10445,7 +10486,7 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[431],
     /* return matcher indices */ &kMatcherIndices[176],
@@ -10457,7 +10498,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[794],
     /* return matcher indices */ &kMatcherIndices[176],
@@ -10469,7 +10510,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[986],
     /* return matcher indices */ &kMatcherIndices[180],
@@ -10481,7 +10522,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[987],
     /* return matcher indices */ &kMatcherIndices[178],
@@ -10493,7 +10534,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[182],
@@ -10505,7 +10546,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[988],
     /* return matcher indices */ &kMatcherIndices[182],
@@ -10517,7 +10558,7 @@
     /* num parameters */ 6,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[138],
     /* return matcher indices */ &kMatcherIndices[182],
@@ -10529,7 +10570,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[796],
     /* return matcher indices */ &kMatcherIndices[182],
@@ -10541,7 +10582,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[989],
     /* return matcher indices */ &kMatcherIndices[186],
@@ -10553,7 +10594,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[990],
     /* return matcher indices */ &kMatcherIndices[184],
@@ -10565,7 +10606,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[188],
@@ -10577,7 +10618,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[991],
     /* return matcher indices */ &kMatcherIndices[188],
@@ -10589,7 +10630,7 @@
     /* num parameters */ 8,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[49],
     /* return matcher indices */ &kMatcherIndices[188],
@@ -10601,7 +10642,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[798],
     /* return matcher indices */ &kMatcherIndices[188],
@@ -10613,7 +10654,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[992],
     /* return matcher indices */ &kMatcherIndices[192],
@@ -10625,7 +10666,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[993],
     /* return matcher indices */ &kMatcherIndices[190],
@@ -10637,7 +10678,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[194],
@@ -10649,7 +10690,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[994],
     /* return matcher indices */ &kMatcherIndices[194],
@@ -10661,7 +10702,7 @@
     /* num parameters */ 6,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[144],
     /* return matcher indices */ &kMatcherIndices[194],
@@ -10673,7 +10714,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[585],
     /* return matcher indices */ &kMatcherIndices[194],
@@ -10685,7 +10726,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[995],
     /* return matcher indices */ &kMatcherIndices[198],
@@ -10697,7 +10738,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[996],
     /* return matcher indices */ &kMatcherIndices[196],
@@ -10709,7 +10750,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[200],
@@ -10721,7 +10762,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[997],
     /* return matcher indices */ &kMatcherIndices[200],
@@ -10733,7 +10774,7 @@
     /* num parameters */ 9,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[40],
     /* return matcher indices */ &kMatcherIndices[200],
@@ -10745,7 +10786,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[588],
     /* return matcher indices */ &kMatcherIndices[200],
@@ -10757,7 +10798,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[998],
     /* return matcher indices */ &kMatcherIndices[204],
@@ -10769,7 +10810,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[999],
     /* return matcher indices */ &kMatcherIndices[202],
@@ -10781,7 +10822,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[206],
@@ -10793,7 +10834,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1000],
     /* return matcher indices */ &kMatcherIndices[206],
@@ -10805,7 +10846,7 @@
     /* num parameters */ 12,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[16],
     /* return matcher indices */ &kMatcherIndices[206],
@@ -10817,7 +10858,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[591],
     /* return matcher indices */ &kMatcherIndices[206],
@@ -10829,7 +10870,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1001],
     /* return matcher indices */ &kMatcherIndices[210],
@@ -10841,7 +10882,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1002],
     /* return matcher indices */ &kMatcherIndices[208],
@@ -10853,7 +10894,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[212],
@@ -10865,7 +10906,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1003],
     /* return matcher indices */ &kMatcherIndices[212],
@@ -10877,7 +10918,7 @@
     /* num parameters */ 8,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[57],
     /* return matcher indices */ &kMatcherIndices[212],
@@ -10889,7 +10930,7 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[435],
     /* return matcher indices */ &kMatcherIndices[212],
@@ -10901,7 +10942,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1004],
     /* return matcher indices */ &kMatcherIndices[216],
@@ -10913,7 +10954,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1005],
     /* return matcher indices */ &kMatcherIndices[214],
@@ -10925,7 +10966,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[218],
@@ -10937,7 +10978,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1006],
     /* return matcher indices */ &kMatcherIndices[218],
@@ -10949,7 +10990,7 @@
     /* num parameters */ 12,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[28],
     /* return matcher indices */ &kMatcherIndices[218],
@@ -10961,7 +11002,7 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[439],
     /* return matcher indices */ &kMatcherIndices[218],
@@ -10973,7 +11014,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1007],
     /* return matcher indices */ &kMatcherIndices[222],
@@ -10985,7 +11026,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1008],
     /* return matcher indices */ &kMatcherIndices[220],
@@ -10997,7 +11038,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[224],
@@ -11009,7 +11050,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1009],
     /* return matcher indices */ &kMatcherIndices[224],
@@ -11021,7 +11062,7 @@
     /* num parameters */ 16,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[0],
     /* return matcher indices */ &kMatcherIndices[224],
@@ -11033,7 +11074,7 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[443],
     /* return matcher indices */ &kMatcherIndices[224],
@@ -11045,7 +11086,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1010],
     /* return matcher indices */ &kMatcherIndices[228],
@@ -11057,7 +11098,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1011],
     /* return matcher indices */ &kMatcherIndices[226],
@@ -11093,7 +11134,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[931],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -11105,7 +11146,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[932],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -11117,7 +11158,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[933],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -11129,7 +11170,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[672],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11141,7 +11182,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[674],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11153,7 +11194,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[676],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11165,7 +11206,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[678],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11177,7 +11218,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[680],
     /* return matcher indices */ &kMatcherIndices[14],
@@ -11189,7 +11230,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[682],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11201,7 +11242,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[684],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11213,7 +11254,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[686],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11225,7 +11266,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[688],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11237,7 +11278,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[690],
     /* return matcher indices */ &kMatcherIndices[14],
@@ -11249,7 +11290,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[710],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11261,7 +11302,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[712],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11273,7 +11314,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[714],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11285,7 +11326,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[716],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11297,7 +11338,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[718],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11309,7 +11350,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[720],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11321,7 +11362,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[722],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11333,7 +11374,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[724],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11345,7 +11386,7 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[730],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -11357,7 +11398,7 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[732],
     /* return matcher indices */ &kMatcherIndices[33],
@@ -11393,7 +11434,7 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[738],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -11405,7 +11446,7 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[740],
     /* return matcher indices */ &kMatcherIndices[33],
@@ -11441,7 +11482,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[468],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11453,7 +11494,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[471],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11465,7 +11506,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[474],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11513,7 +11554,7 @@
     /* num parameters */ 0,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[9],
@@ -11525,7 +11566,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[954],
     /* return matcher indices */ &kMatcherIndices[9],
@@ -11549,7 +11590,7 @@
     /* num parameters */ 0,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -11561,7 +11602,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[956],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -11585,7 +11626,7 @@
     /* num parameters */ 0,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -11597,7 +11638,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[958],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -11621,7 +11662,7 @@
     /* num parameters */ 0,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[1],
@@ -11633,7 +11674,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[960],
     /* return matcher indices */ &kMatcherIndices[1],
@@ -11657,7 +11698,7 @@
     /* num parameters */ 0,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -11669,7 +11710,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[962],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -11693,7 +11734,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[800],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11705,7 +11746,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[801],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11717,7 +11758,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[802],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11729,7 +11770,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[803],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11741,7 +11782,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[804],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11753,7 +11794,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[805],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11765,7 +11806,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[806],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -11777,7 +11818,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[807],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -11789,7 +11830,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[808],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -11801,7 +11842,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[809],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -11813,7 +11854,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[811],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11825,7 +11866,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[812],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11837,7 +11878,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[813],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11849,7 +11890,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[814],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11861,7 +11902,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[815],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11873,7 +11914,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[816],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11885,7 +11926,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[594],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11897,7 +11938,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[596],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11909,7 +11950,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[817],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11921,7 +11962,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[818],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11933,7 +11974,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[819],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11945,7 +11986,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[820],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11957,7 +11998,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[447],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11969,7 +12010,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[450],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11981,7 +12022,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[821],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -11993,7 +12034,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[822],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12005,7 +12046,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[823],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12017,7 +12058,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[824],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12029,7 +12070,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[825],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12041,7 +12082,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[826],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12053,7 +12094,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[827],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12065,7 +12106,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[828],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12077,7 +12118,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[829],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12089,7 +12130,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[830],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12101,7 +12142,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[831],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12113,7 +12154,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[832],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12125,7 +12166,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[600],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12137,7 +12178,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[602],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12149,7 +12190,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[834],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12161,7 +12202,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[835],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12173,7 +12214,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[836],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12185,7 +12226,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[837],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12197,7 +12238,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[838],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12209,7 +12250,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[839],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12221,7 +12262,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[840],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12233,7 +12274,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[841],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12245,7 +12286,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[842],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12257,7 +12298,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[843],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12269,7 +12310,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[844],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12281,7 +12322,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[845],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12293,7 +12334,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[846],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12305,7 +12346,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[847],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12317,7 +12358,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[848],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12329,7 +12370,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[849],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12341,7 +12382,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[453],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12353,7 +12394,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[456],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12365,7 +12406,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[850],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12377,7 +12418,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[851],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12389,7 +12430,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[852],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12401,7 +12442,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[853],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12413,7 +12454,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[854],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12425,7 +12466,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[855],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12437,7 +12478,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[462],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12449,7 +12490,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[465],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12461,7 +12502,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[856],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12473,7 +12514,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[857],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12485,7 +12526,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[858],
     /* return matcher indices */ &kMatcherIndices[104],
@@ -12497,7 +12538,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[859],
     /* return matcher indices */ &kMatcherIndices[39],
@@ -12509,7 +12550,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[860],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12521,7 +12562,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[861],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12533,7 +12574,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[862],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12545,7 +12586,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[863],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12557,7 +12598,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[864],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12569,7 +12610,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[865],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12581,7 +12622,7 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[295],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12593,7 +12634,7 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[299],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12605,7 +12646,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[866],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12617,7 +12658,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[867],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12627,33 +12668,33 @@
   {
     /* [361] */
     /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[610],
     /* return matcher indices */ &kMatcherIndices[3],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::ldexp,
   },
   {
     /* [362] */
     /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[612],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::ldexp,
   },
   {
     /* [363] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[868],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12665,7 +12706,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[869],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12677,7 +12718,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[870],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12689,7 +12730,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[871],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12701,7 +12742,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[872],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12713,7 +12754,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[873],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12725,7 +12766,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[614],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12737,7 +12778,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[616],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12749,7 +12790,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[618],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12761,7 +12802,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[620],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12773,7 +12814,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[874],
     /* return matcher indices */ &kMatcherIndices[106],
@@ -12785,7 +12826,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[875],
     /* return matcher indices */ &kMatcherIndices[45],
@@ -12797,7 +12838,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[622],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12809,7 +12850,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[624],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12821,7 +12862,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[882],
     /* return matcher indices */ &kMatcherIndices[38],
@@ -12833,7 +12874,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[883],
     /* return matcher indices */ &kMatcherIndices[36],
@@ -12845,7 +12886,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[884],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12857,7 +12898,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[885],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12869,7 +12910,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[886],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12881,7 +12922,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[887],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12893,7 +12934,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[888],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12905,7 +12946,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[889],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12917,7 +12958,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[890],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12929,7 +12970,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[891],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12965,7 +13006,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[894],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -12977,7 +13018,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[895],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12989,7 +13030,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[896],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13001,7 +13042,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[897],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13013,7 +13054,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[489],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13025,7 +13066,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[492],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13037,7 +13078,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[898],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13049,7 +13090,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[899],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13061,7 +13102,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[628],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13073,7 +13114,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[630],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13085,7 +13126,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[900],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13097,7 +13138,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[901],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13109,7 +13150,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[902],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13121,7 +13162,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[903],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13133,7 +13174,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[905],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13145,7 +13186,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[906],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13169,7 +13210,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[945],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -13181,7 +13222,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[519],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -13193,7 +13234,7 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[522],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -13205,7 +13246,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[948],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -13217,7 +13258,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[949],
     /* return matcher indices */ &kMatcherIndices[33],
@@ -13349,7 +13390,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[758],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -13361,7 +13402,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[760],
     /* return matcher indices */ &kMatcherIndices[33],
@@ -13373,7 +13414,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[762],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -13385,7 +13426,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[764],
     /* return matcher indices */ &kMatcherIndices[33],
@@ -13397,7 +13438,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[766],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -13409,7 +13450,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[768],
     /* return matcher indices */ &kMatcherIndices[33],
@@ -13421,7 +13462,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[770],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -13493,7 +13534,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[8],
     /* parameters */ &kParameters[810],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -13505,7 +13546,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[598],
     /* return matcher indices */ &kMatcherIndices[102],
@@ -13517,7 +13558,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[833],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13529,7 +13570,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[604],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13541,7 +13582,7 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[606],
     /* return matcher indices */ &kMatcherIndices[9],
@@ -13553,7 +13594,7 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[608],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -13565,7 +13606,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[459],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13577,7 +13618,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[876],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13589,7 +13630,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[877],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -13601,7 +13642,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[878],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -13613,7 +13654,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[879],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -13625,7 +13666,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[880],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -13637,7 +13678,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[881],
     /* return matcher indices */ &kMatcherIndices[101],
@@ -13649,7 +13690,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[626],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13661,7 +13702,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[4],
     /* parameters */ &kParameters[477],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -13673,7 +13714,7 @@
     /* num parameters */ 0,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ nullptr,
@@ -13685,7 +13726,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[3],
     /* parameters */ &kParameters[904],
     /* return matcher indices */ &kMatcherIndices[18],
@@ -13697,7 +13738,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[907],
     /* return matcher indices */ &kMatcherIndices[108],
@@ -13709,7 +13750,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[908],
     /* return matcher indices */ &kMatcherIndices[108],
@@ -13721,7 +13762,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[909],
     /* return matcher indices */ &kMatcherIndices[108],
@@ -13733,7 +13774,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[910],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -13745,7 +13786,7 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[911],
     /* return matcher indices */ &kMatcherIndices[110],
@@ -13757,7 +13798,7 @@
     /* num parameters */ 0,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1012],
     /* return matcher indices */ nullptr,
@@ -13769,7 +13810,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[946],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13781,7 +13822,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[654],
     /* return matcher indices */ nullptr,
@@ -13793,7 +13834,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[656],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13805,7 +13846,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[658],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13817,7 +13858,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[660],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13829,7 +13870,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[662],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13841,7 +13882,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[664],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13853,7 +13894,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[666],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13865,7 +13906,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[668],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13877,7 +13918,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[670],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13889,7 +13930,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[9],
     /* parameters */ &kParameters[570],
     /* return matcher indices */ &kMatcherIndices[160],
@@ -13901,7 +13942,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[947],
     /* return matcher indices */ &kMatcherIndices[3],
@@ -13913,7 +13954,7 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[746],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -13925,7 +13966,7 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[748],
     /* return matcher indices */ &kMatcherIndices[35],
@@ -14252,8 +14293,8 @@
   },
   {
     /* [46] */
-    /* fn ldexp<T : f32_f16>(T, i32) -> T */
-    /* fn ldexp<N : num, T : f32_f16>(vec<N, T>, vec<N, i32>) -> vec<N, T> */
+    /* fn ldexp<T : fa_f32_f16, U : ia_i32>(T, U) -> T */
+    /* fn ldexp<N : num, T : fa_f32_f16, U : ia_i32>(vec<N, T>, vec<N, U>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[361],
   },
diff --git a/src/tint/resolver/load_test.cc b/src/tint/resolver/load_test.cc
new file mode 100644
index 0000000..fea60ff
--- /dev/null
+++ b/src/tint/resolver/load_test.cc
@@ -0,0 +1,370 @@
+// 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/resolver.h"
+#include "src/tint/resolver/resolver_test_helper.h"
+#include "src/tint/sem/test_helper.h"
+
+#include "src/tint/sem/load.h"
+#include "src/tint/type/reference.h"
+
+#include "gmock/gmock.h"
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+using ResolverLoadTest = ResolverTest;
+
+TEST_F(ResolverLoadTest, VarInitializer) {
+    // var ref = 1i;
+    // var v = ref;
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   Var("v", ident));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::I32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
+}
+
+TEST_F(ResolverLoadTest, LetInitializer) {
+    // var ref = 1i;
+    // let l = ref;
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   Let("l", ident));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::I32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
+}
+
+TEST_F(ResolverLoadTest, Assignment) {
+    // var ref = 1i;
+    // var v : i32;
+    // v = ref;
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   Var("v", ty.i32()),     //
+                   Assign("v", ident));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::I32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
+}
+
+TEST_F(ResolverLoadTest, CompoundAssignment) {
+    // var ref = 1i;
+    // var v : i32;
+    // v += ref;
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   Var("v", ty.i32()),     //
+                   CompoundAssign("v", ident, ast::BinaryOp::kAdd));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::I32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
+}
+
+TEST_F(ResolverLoadTest, UnaryOp) {
+    // var ref = 1i;
+    // var v = -ref;
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   Var("v", Negation(ident)));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::I32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
+}
+
+TEST_F(ResolverLoadTest, UnaryOp_NoLoad) {
+    // var ref = 1i;
+    // let v = &ref;
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   Let("v", AddressOf(ident)));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* var_user = Sem().Get<sem::VariableUser>(ident);
+    ASSERT_NE(var_user, nullptr);
+    EXPECT_TRUE(var_user->Type()->Is<type::Reference>());
+    EXPECT_TRUE(var_user->Type()->UnwrapRef()->Is<type::I32>());
+}
+
+TEST_F(ResolverLoadTest, BinaryOp) {
+    // var ref = 1i;
+    // var v = ref * 1i;
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   Var("v", Mul(ident, 1_i)));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::I32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
+}
+
+TEST_F(ResolverLoadTest, Index) {
+    // var ref = 1i;
+    // var v = array<i32, 3>(1i, 2i, 3i)[ref];
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   IndexAccessor(array<i32, 3>(1_i, 2_i, 3_i), ident));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::I32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
+}
+
+TEST_F(ResolverLoadTest, MultiComponentSwizzle) {
+    // var ref = vec4(1);
+    // var v = ref.xyz;
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Construct(ty.vec4<i32>(), 1_i)),  //
+                   Var("v", MemberAccessor(ident, "xyz")));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::Vector>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Vector>());
+}
+
+TEST_F(ResolverLoadTest, Bitcast) {
+    // var ref = 1f;
+    // var v = bitcast<i32>(ref);
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_f)),  //
+                   Bitcast<i32>(ident));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::F32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
+}
+
+TEST_F(ResolverLoadTest, BuiltinArg) {
+    // var ref = 1f;
+    // var v = abs(ref);
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_f)),  //
+                   Call("abs", ident));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::F32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
+}
+
+TEST_F(ResolverLoadTest, FunctionArg) {
+    // fn f(x : f32) {}
+    // var ref = 1f;
+    // f(ref);
+    Func("f", utils::Vector{Param("x", ty.f32())}, ty.void_(), utils::Empty);
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_f)),  //
+                   CallStmt(Call("f", ident)));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::F32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
+}
+
+TEST_F(ResolverLoadTest, FunctionArg_Handles) {
+    // @group(0) @binding(0) var t : texture_2d<f32>;
+    // @group(0) @binding(1) var s : sampler;
+    // fn f(tp : texture_2d<f32>, sp : sampler) -> vec4<f32> {
+    //   return textureSampleLevel(tp, sp, vec2(), 0);
+    // }
+    // f(t, s);
+    GlobalVar("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+              utils::Vector{Group(0_a), Binding(0_a)});
+    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), utils::Vector{Group(0_a), Binding(1_a)});
+    Func("f",
+         utils::Vector{
+             Param("tp", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32())),
+             Param("sp", ty.sampler(ast::SamplerKind::kSampler)),
+         },
+         ty.vec4<f32>(),
+         utils::Vector{
+             Return(Call("textureSampleLevel", "tp", "sp", Construct(ty.vec2<f32>()), 0_a)),
+         });
+    auto* t_ident = Expr("t");
+    auto* s_ident = Expr("s");
+    WrapInFunction(CallStmt(Call("f", t_ident, s_ident)));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    {
+        auto* load = Sem().Get<sem::Load>(t_ident);
+        ASSERT_NE(load, nullptr);
+        EXPECT_TRUE(load->Type()->Is<type::SampledTexture>());
+        EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+        EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::SampledTexture>());
+    }
+    {
+        auto* load = Sem().Get<sem::Load>(s_ident);
+        ASSERT_NE(load, nullptr);
+        EXPECT_TRUE(load->Type()->Is<type::Sampler>());
+        EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+        EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Sampler>());
+    }
+}
+
+TEST_F(ResolverLoadTest, FunctionReturn) {
+    // var ref = 1f;
+    // return ref;
+    auto* ident = Expr("ref");
+    Func("f", utils::Empty, ty.f32(),
+         utils::Vector{
+             Decl(Var("ref", Expr(1_f))),
+             Return(ident),
+         });
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::F32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
+}
+
+TEST_F(ResolverLoadTest, IfCond) {
+    // var ref = false;
+    // if (ref) {}
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(false)),  //
+                   If(ident, Block()));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::Bool>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
+}
+
+TEST_F(ResolverLoadTest, Switch) {
+    // var ref = 1i;
+    // switch (ref) {
+    //   default:
+    // }
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   Switch(ident, DefaultCase()));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::I32>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
+}
+
+TEST_F(ResolverLoadTest, BreakIfCond) {
+    // var ref = false;
+    // loop {
+    //   continuing {
+    //     break if (ref);
+    //   }
+    // }
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(false)),  //
+                   Loop(Block(), Block(BreakIf(ident))));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::Bool>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
+}
+
+TEST_F(ResolverLoadTest, ForCond) {
+    // var ref = false;
+    // for (; ref; ) {}
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(false)),  //
+                   For(nullptr, ident, nullptr, Block()));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::Bool>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
+}
+
+TEST_F(ResolverLoadTest, WhileCond) {
+    // var ref = false;
+    // while (ref) {}
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(false)),  //
+                   While(ident, Block()));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* load = Sem().Get<sem::Load>(ident);
+    ASSERT_NE(load, nullptr);
+    EXPECT_TRUE(load->Type()->Is<type::Bool>());
+    EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
+    EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
+}
+
+TEST_F(ResolverLoadTest, AddressOf) {
+    // var ref = 1i;
+    // let l = &ref;
+    auto* ident = Expr("ref");
+    WrapInFunction(Var("ref", Expr(1_i)),  //
+                   Let("l", AddressOf(ident)));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    auto* no_load = Sem().Get(ident);
+    ASSERT_NE(no_load, nullptr);
+    EXPECT_TRUE(no_load->Type()->Is<type::Reference>());  // No load
+}
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/ptr_ref_test.cc b/src/tint/resolver/ptr_ref_test.cc
index ecaafa7..4176423 100644
--- a/src/tint/resolver/ptr_ref_test.cc
+++ b/src/tint/resolver/ptr_ref_test.cc
@@ -14,6 +14,7 @@
 
 #include "src/tint/resolver/resolver.h"
 #include "src/tint/resolver/resolver_test_helper.h"
+#include "src/tint/sem/load.h"
 #include "src/tint/type/reference.h"
 
 #include "gmock/gmock.h"
@@ -52,8 +53,14 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    ASSERT_TRUE(TypeOf(expr)->Is<type::Reference>());
-    EXPECT_TRUE(TypeOf(expr)->As<type::Reference>()->StoreType()->Is<type::I32>());
+    auto* load = Sem().Get<sem::Load>(expr);
+    ASSERT_NE(load, nullptr);
+
+    auto* ref = load->Reference();
+    ASSERT_NE(ref, nullptr);
+
+    ASSERT_TRUE(ref->Type()->Is<type::Reference>());
+    EXPECT_TRUE(ref->Type()->As<type::Reference>()->StoreType()->Is<type::I32>());
 }
 
 TEST_F(ResolverPtrRefTest, DefaultPtrAddressSpace) {
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index f0cbada..5d9dfca 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -58,6 +58,7 @@
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/if_statement.h"
 #include "src/tint/sem/index_accessor_expression.h"
+#include "src/tint/sem/load.h"
 #include "src/tint/sem/loop_statement.h"
 #include "src/tint/sem/materialize.h"
 #include "src/tint/sem/member_accessor_expression.h"
@@ -379,13 +380,11 @@
         return nullptr;
     }
 
-    auto* rhs = Materialize(Expression(v->initializer), ty);
+    auto* rhs = Load(Materialize(Expression(v->initializer), ty));
     if (!rhs) {
         return nullptr;
     }
 
-    RegisterLoadIfNeeded(rhs);
-
     // If the variable has no declared type, infer it from the RHS
     if (!ty) {
         ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
@@ -432,8 +431,11 @@
 
     const sem::Expression* rhs = nullptr;
 
-    // Does the variable have a initializer?
+    // Does the variable have an initializer?
     if (v->initializer) {
+        // Note: RHS must be a const or override expression, which excludes references.
+        // So there's no need to load or unwrap references here.
+
         ExprEvalStageConstraint constraint{sem::EvaluationStage::kOverride, "override initializer"};
         TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
         rhs = Materialize(Expression(v->initializer), ty);
@@ -443,7 +445,7 @@
 
         // If the variable has no declared type, infer it from the RHS
         if (!ty) {
-            ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
+            ty = rhs->Type();
         }
     } else if (!ty) {
         AddError("override declaration requires a type or initializer", v->source);
@@ -529,6 +531,9 @@
         }
     }
 
+    // Note: RHS must be a const expression, which excludes references.
+    // So there's no need to load or unwrap references here.
+
     if (ty) {
         // If an explicit type was specified, materialize to that type
         rhs = Materialize(rhs, ty);
@@ -584,16 +589,14 @@
         };
         TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
 
-        rhs = Materialize(Expression(var->initializer), storage_ty);
+        rhs = Load(Materialize(Expression(var->initializer), storage_ty));
         if (!rhs) {
             return nullptr;
         }
         // If the variable has no declared type, infer it from the RHS
         if (!storage_ty) {
-            storage_ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
+            storage_ty = rhs->Type();
         }
-
-        RegisterLoadIfNeeded(rhs);
     }
 
     if (!storage_ty) {
@@ -1315,7 +1318,7 @@
     auto* sem =
         builder_->create<sem::IfStatement>(stmt, current_compound_statement_, current_function_);
     return StatementScope(stmt, sem, [&] {
-        auto* cond = Expression(stmt->condition);
+        auto* cond = Load(Expression(stmt->condition));
         if (!cond) {
             return false;
         }
@@ -1323,8 +1326,6 @@
         sem->Behaviors() = cond->Behaviors();
         sem->Behaviors().Remove(sem::Behavior::kNext);
 
-        RegisterLoadIfNeeded(cond);
-
         Mark(stmt->body);
         auto* body = builder_->create<sem::BlockStatement>(stmt->body, current_compound_statement_,
                                                            current_function_);
@@ -1412,14 +1413,12 @@
         }
 
         if (auto* cond_expr = stmt->condition) {
-            auto* cond = Expression(cond_expr);
+            auto* cond = Load(Expression(cond_expr));
             if (!cond) {
                 return false;
             }
             sem->SetCondition(cond);
             behaviors.Add(cond->Behaviors());
-
-            RegisterLoadIfNeeded(cond);
         }
 
         if (auto* continuing = stmt->continuing) {
@@ -1457,15 +1456,13 @@
     return StatementScope(stmt, sem, [&] {
         auto& behaviors = sem->Behaviors();
 
-        auto* cond = Expression(stmt->condition);
+        auto* cond = Load(Expression(stmt->condition));
         if (!cond) {
             return false;
         }
         sem->SetCondition(cond);
         behaviors.Add(cond->Behaviors());
 
-        RegisterLoadIfNeeded(cond);
-
         Mark(stmt->body);
 
         auto* body = builder_->create<sem::LoopBlockStatement>(
@@ -1592,26 +1589,6 @@
     return nullptr;
 }
 
-void Resolver::RegisterLoadIfNeeded(const sem::Expression* expr) {
-    if (!expr) {
-        return;
-    }
-    if (!expr->Type()->Is<type::Reference>()) {
-        return;
-    }
-    if (!current_function_) {
-        // There is currently no situation where the Load Rule can be invoked outside of a function.
-        return;
-    }
-    auto& info = alias_analysis_infos_[current_function_];
-    Switch(
-        expr->RootIdentifier(),
-        [&](const sem::GlobalVariable* global) {
-            info.module_scope_reads.insert({global, expr});
-        },
-        [&](const sem::Parameter* param) { info.parameter_reads.insert(param); });
-}
-
 void Resolver::RegisterStore(const sem::Expression* expr) {
     auto& info = alias_analysis_infos_[current_function_];
     Switch(
@@ -1778,6 +1755,33 @@
         });
 }
 
+const sem::Expression* Resolver::Load(const sem::Expression* expr) {
+    if (!expr) {
+        // Allow for Load(Expression(blah)), where failures pass through Load()
+        return nullptr;
+    }
+
+    if (!expr->Type()->Is<type::Reference>()) {
+        // Expression is not a reference type, so cannot be loaded. Just return expr.
+        return expr;
+    }
+
+    auto* load = builder_->create<sem::Load>(expr, current_statement_);
+    load->Behaviors() = expr->Behaviors();
+    builder_->Sem().Replace(expr->Declaration(), load);
+
+    // Track the load for the alias analysis.
+    auto& alias_info = alias_analysis_infos_[current_function_];
+    Switch(
+        expr->RootIdentifier(),
+        [&](const sem::GlobalVariable* global) {
+            alias_info.module_scope_reads.insert({global, expr});
+        },
+        [&](const sem::Parameter* param) { alias_info.parameter_reads.insert(param); });
+
+    return load;
+}
+
 const sem::Expression* Resolver::Materialize(const sem::Expression* expr,
                                              const type::Type* target_type /* = nullptr */) {
     if (!expr) {
@@ -1829,8 +1833,8 @@
 }
 
 template <size_t N>
-bool Resolver::MaybeMaterializeArguments(utils::Vector<const sem::Expression*, N>& args,
-                                         const sem::CallTarget* target) {
+bool Resolver::MaybeMaterializeAndLoadArguments(utils::Vector<const sem::Expression*, N>& args,
+                                                const sem::CallTarget* target) {
     for (size_t i = 0, n = std::min(args.Length(), target->Parameters().Length()); i < n; i++) {
         const auto* param_ty = target->Parameters()[i]->Type();
         if (ShouldMaterializeArgument(param_ty)) {
@@ -1840,6 +1844,13 @@
             }
             args[i] = materialized;
         }
+        if (!param_ty->Is<type::Reference>()) {
+            auto* load = Load(args[i]);
+            if (!load) {
+                return false;
+            }
+            args[i] = load;
+        }
     }
     return true;
 }
@@ -1875,7 +1886,7 @@
 }
 
 sem::Expression* Resolver::IndexAccessor(const ast::IndexAccessorExpression* expr) {
-    auto* idx = Materialize(sem_.Get(expr->index));
+    auto* idx = Load(Materialize(sem_.Get(expr->index)));
     if (!idx) {
         return nullptr;
     }
@@ -1886,7 +1897,6 @@
         //     vec2(1, 2)[runtime-index]
         obj = Materialize(obj);
     }
-    RegisterLoadIfNeeded(idx);
     if (!obj) {
         return nullptr;
     }
@@ -1939,7 +1949,7 @@
 }
 
 sem::Expression* Resolver::Bitcast(const ast::BitcastExpression* expr) {
-    auto* inner = Materialize(sem_.Get(expr->expr));
+    auto* inner = Load(Materialize(sem_.Get(expr->expr)));
     if (!inner) {
         return nullptr;
     }
@@ -1948,8 +1958,6 @@
         return nullptr;
     }
 
-    RegisterLoadIfNeeded(inner);
-
     const constant::Value* val = nullptr;
     // TODO(crbug.com/tint/1582): short circuit 'expr' once const eval of Bitcast is implemented.
     if (auto r = const_eval_.Bitcast(ty, inner)) {
@@ -1990,8 +1998,6 @@
         args.Push(arg);
         args_stage = sem::EarliestStage(args_stage, arg->Stage());
         arg_behaviors.Add(arg->Behaviors());
-
-        RegisterLoadIfNeeded(arg);
     }
     arg_behaviors.Remove(sem::Behavior::kNext);
 
@@ -2008,7 +2014,7 @@
         if (!ctor_or_conv.target) {
             return nullptr;
         }
-        if (!MaybeMaterializeArguments(args, ctor_or_conv.target)) {
+        if (!MaybeMaterializeAndLoadArguments(args, ctor_or_conv.target)) {
             return nullptr;
         }
 
@@ -2037,7 +2043,7 @@
     // initializer call target.
     auto arr_or_str_init = [&](const type::Type* ty,
                                const sem::CallTarget* call_target) -> sem::Call* {
-        if (!MaybeMaterializeArguments(args, call_target)) {
+        if (!MaybeMaterializeAndLoadArguments(args, call_target)) {
             return nullptr;
         }
 
@@ -2325,7 +2331,7 @@
         }
     } else {
         // Materialize arguments if the parameter type is not abstract
-        if (!MaybeMaterializeArguments(args, builtin.sem)) {
+        if (!MaybeMaterializeAndLoadArguments(args, builtin.sem)) {
             return nullptr;
         }
     }
@@ -2476,14 +2482,17 @@
     if (texture_index == -1) {
         TINT_ICE(Resolver, diagnostics_) << "texture builtin without texture parameter";
     }
-    if (auto* user = args[static_cast<size_t>(texture_index)]->As<sem::VariableUser>()) {
+    if (auto* user =
+            args[static_cast<size_t>(texture_index)]->UnwrapLoad()->As<sem::VariableUser>()) {
         auto* texture = user->Variable();
         if (!texture->Type()->UnwrapRef()->Is<type::StorageTexture>()) {
             int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
-            const sem::Variable* sampler =
-                sampler_index != -1
-                    ? args[static_cast<size_t>(sampler_index)]->As<sem::VariableUser>()->Variable()
-                    : nullptr;
+            const sem::Variable* sampler = sampler_index != -1
+                                               ? args[static_cast<size_t>(sampler_index)]
+                                                     ->UnwrapLoad()
+                                                     ->As<sem::VariableUser>()
+                                                     ->Variable()
+                                               : nullptr;
             current_function_->AddTextureSamplerPair(texture, sampler);
         }
     }
@@ -2497,7 +2506,7 @@
     auto sym = expr->target.name->symbol;
     auto name = builder_->Symbols().NameFor(sym);
 
-    if (!MaybeMaterializeArguments(args, target)) {
+    if (!MaybeMaterializeAndLoadArguments(args, target)) {
         return nullptr;
     }
 
@@ -2554,11 +2563,11 @@
         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();
+            texture = args[param->Index()]->UnwrapLoad()->As<sem::VariableUser>()->Variable();
         }
         if (sampler) {
             if (auto* param = sampler->As<sem::Parameter>()) {
-                sampler = args[param->Index()]->As<sem::VariableUser>()->Variable();
+                sampler = args[param->Index()]->UnwrapLoad()->As<sem::VariableUser>()->Variable();
             }
         }
         current_function_->AddTextureSamplerPair(texture, sampler);
@@ -2820,6 +2829,7 @@
                 return nullptr;
             }
 
+            const sem::Expression* obj_expr = object;
             if (size == 1) {
                 // A single element swizzle is just the type of the vector.
                 ty = vec->type();
@@ -2831,12 +2841,15 @@
                 // The vector will have a number of components equal to the length of
                 // the swizzle.
                 ty = builder_->create<type::Vector>(vec->type(), static_cast<uint32_t>(size));
+
+                // The load rule is invoked before the swizzle, if necessary.
+                obj_expr = Load(object);
             }
             auto val = const_eval_.Swizzle(ty, object, swizzle);
             if (!val) {
                 return nullptr;
             }
-            return builder_->create<sem::Swizzle>(expr, ty, current_statement_, val.Get(), object,
+            return builder_->create<sem::Swizzle>(expr, ty, current_statement_, val.Get(), obj_expr,
                                                   std::move(swizzle), has_side_effects, root_ident);
         },
 
@@ -2872,8 +2885,15 @@
         }
     }
 
-    RegisterLoadIfNeeded(lhs);
-    RegisterLoadIfNeeded(rhs);
+    // Load arguments if they are references
+    lhs = Load(lhs);
+    if (!lhs) {
+        return nullptr;
+    }
+    rhs = Load(rhs);
+    if (!rhs) {
+        return nullptr;
+    }
 
     const constant::Value* value = nullptr;
     if (stage == sem::EvaluationStage::kConstant) {
@@ -2975,6 +2995,14 @@
                     return nullptr;
                 }
             }
+
+            // Load expr if it is a reference
+            expr = Load(expr);
+            if (!expr) {
+                return nullptr;
+            }
+
+            stage = expr->Stage();
             if (stage == sem::EvaluationStage::kConstant) {
                 if (op.const_eval_fn) {
                     if (auto r = (const_eval_.*op.const_eval_fn)(
@@ -2988,7 +3016,6 @@
                     stage = sem::EvaluationStage::kRuntime;
                 }
             }
-            RegisterLoadIfNeeded(expr);
             break;
         }
     }
@@ -3437,7 +3464,7 @@
 
         const type::Type* value_ty = nullptr;
         if (auto* value = stmt->value) {
-            const auto* expr = Expression(value);
+            const auto* expr = Load(Expression(value));
             if (!expr) {
                 return false;
             }
@@ -3448,9 +3475,8 @@
                 }
             }
             behaviors.Add(expr->Behaviors() - sem::Behavior::kNext);
-            value_ty = expr->Type()->UnwrapRef();
 
-            RegisterLoadIfNeeded(expr);
+            value_ty = expr->Type();
         } else {
             value_ty = builder_->create<type::Void>();
         }
@@ -3468,15 +3494,13 @@
     return StatementScope(stmt, sem, [&] {
         auto& behaviors = sem->Behaviors();
 
-        const auto* cond = Expression(stmt->condition);
+        const auto* cond = Load(Expression(stmt->condition));
         if (!cond) {
             return false;
         }
         behaviors = cond->Behaviors() - sem::Behavior::kNext;
 
-        RegisterLoadIfNeeded(cond);
-
-        auto* cond_ty = cond->Type()->UnwrapRef();
+        auto* cond_ty = cond->Type();
 
         // Determine the common type across all selectors and the switch expression
         // This must materialize to an integer scalar (non-abstract).
@@ -3579,7 +3603,10 @@
             }
         }
 
-        RegisterLoadIfNeeded(rhs);
+        rhs = Load(rhs);
+        if (!rhs) {
+            return false;
+        }
 
         auto& behaviors = sem->Behaviors();
         behaviors = rhs->Behaviors();
@@ -3609,7 +3636,7 @@
     auto* sem = builder_->create<sem::BreakIfStatement>(stmt, current_compound_statement_,
                                                         current_function_);
     return StatementScope(stmt, sem, [&] {
-        auto* cond = Expression(stmt->condition);
+        auto* cond = Load(Expression(stmt->condition));
         if (!cond) {
             return false;
         }
@@ -3617,8 +3644,6 @@
         sem->Behaviors() = cond->Behaviors();
         sem->Behaviors().Add(sem::Behavior::kBreak);
 
-        RegisterLoadIfNeeded(cond);
-
         return validator_.BreakIfStatement(sem, current_statement_);
     });
 }
@@ -3645,12 +3670,11 @@
             return false;
         }
 
-        auto* rhs = Expression(stmt->rhs);
+        auto* rhs = Load(Expression(stmt->rhs));
         if (!rhs) {
             return false;
         }
 
-        RegisterLoadIfNeeded(rhs);
         RegisterStore(lhs);
 
         sem->Behaviors() = rhs->Behaviors() + lhs->Behaviors();
@@ -3705,7 +3729,6 @@
         }
         sem->Behaviors() = lhs->Behaviors();
 
-        RegisterLoadIfNeeded(lhs);
         RegisterStore(lhs);
 
         return validator_.IncrementDecrementStatement(stmt);
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 8b16abf..1a29a75 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -157,10 +157,6 @@
     sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
     sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
 
-    /// Register a memory load from an expression, to track accesses to root identifiers in order to
-    /// perform alias analysis.
-    void RegisterLoadIfNeeded(const sem::Expression* expr);
-
     /// Register a memory store to an expression, to track accesses to root identifiers in order to
     /// perform alias analysis.
     void RegisterStore(const sem::Expression* expr);
@@ -169,8 +165,11 @@
     /// @returns true is the call arguments are free from aliasing issues, false otherwise.
     bool AliasAnalysis(const sem::Call* call);
 
+    /// If `expr` is of a reference type, then Load will create and return a sem::Load node wrapping
+    /// `expr`. If `expr` is not of a reference type, then Load will just return `expr`.
+    const sem::Expression* Load(const sem::Expression* expr);
+
     /// If `expr` is not of an abstract-numeric type, then Materialize() will just return `expr`.
-    /// If `expr` is of an abstract-numeric type:
     /// * Materialize will create and return a sem::Materialize node wrapping `expr`.
     /// * The AST -> Sem binding will be updated to point to the new sem::Materialize node.
     /// * The sem::Materialize node will have a new concrete type, which will be `target_type` if
@@ -181,15 +180,19 @@
     ///       if `expr` has a element type of abstract-float.
     /// * The sem::Materialize constant value will be the value of `expr` value-converted to the
     ///   materialized type.
+    /// If `expr` is not of an abstract-numeric type, then Materialize() will just return `expr`.
     /// If `expr` is nullptr, then Materialize() will also return nullptr.
     const sem::Expression* Materialize(const sem::Expression* expr,
                                        const type::Type* target_type = nullptr);
 
-    /// Materializes all the arguments in `args` to the parameter types of `target`.
+    /// For each argument in `args`:
+    /// * Calls Materialize() passing the argument and the corresponding parameter type.
+    /// * Calls Load() passing the argument, iff the corresponding parameter type is not a
+    ///   reference type.
     /// @returns true on success, false on failure.
     template <size_t N>
-    bool MaybeMaterializeArguments(utils::Vector<const sem::Expression*, N>& args,
-                                   const sem::CallTarget* target);
+    bool MaybeMaterializeAndLoadArguments(utils::Vector<const sem::Expression*, N>& args,
+                                          const sem::CallTarget* target);
 
     /// @returns true if an argument of an abstract numeric type, passed to a parameter of type
     /// `parameter_ty` should be materialized.
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index 3c8e3a4..e5538c5 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -707,8 +707,7 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     ASSERT_NE(TypeOf(ident), nullptr);
-    ASSERT_TRUE(TypeOf(ident)->Is<type::Reference>());
-    EXPECT_TRUE(TypeOf(ident)->UnwrapRef()->Is<type::F32>());
+    EXPECT_TRUE(TypeOf(ident)->Is<type::F32>());
     EXPECT_TRUE(CheckVarUsers(my_var, utils::Vector{ident}));
     ASSERT_NE(VarOf(ident), nullptr);
     EXPECT_EQ(VarOf(ident)->Declaration(), my_var);
@@ -788,8 +787,7 @@
     EXPECT_TRUE(TypeOf(my_var_a)->UnwrapRef()->Is<type::F32>());
     EXPECT_EQ(StmtOf(my_var_a), assign);
     ASSERT_NE(TypeOf(my_var_b), nullptr);
-    ASSERT_TRUE(TypeOf(my_var_b)->Is<type::Reference>());
-    EXPECT_TRUE(TypeOf(my_var_b)->UnwrapRef()->Is<type::F32>());
+    EXPECT_TRUE(TypeOf(my_var_b)->Is<type::F32>());
     EXPECT_EQ(StmtOf(my_var_b), assign);
     EXPECT_TRUE(CheckVarUsers(var, utils::Vector{my_var_a, my_var_b}));
     ASSERT_NE(VarOf(my_var_a), nullptr);
@@ -1250,11 +1248,8 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     ASSERT_NE(TypeOf(mem), nullptr);
-    ASSERT_TRUE(TypeOf(mem)->Is<type::Reference>());
-
-    auto* ref = TypeOf(mem)->As<type::Reference>();
-    EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
-    auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
+    EXPECT_TRUE(TypeOf(mem)->Is<type::F32>());
+    auto* sma = Sem().Get(mem)->UnwrapLoad()->As<sem::StructMemberAccess>();
     ASSERT_NE(sma, nullptr);
     EXPECT_TRUE(sma->Member()->Type()->Is<type::F32>());
     EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
@@ -1274,11 +1269,8 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     ASSERT_NE(TypeOf(mem), nullptr);
-    ASSERT_TRUE(TypeOf(mem)->Is<type::Reference>());
-
-    auto* ref = TypeOf(mem)->As<type::Reference>();
-    EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
-    auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
+    EXPECT_TRUE(TypeOf(mem)->Is<type::F32>());
+    auto* sma = Sem().Get(mem)->UnwrapLoad()->As<sem::StructMemberAccess>();
     ASSERT_NE(sma, nullptr);
     EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
     EXPECT_TRUE(sma->Member()->Type()->Is<type::F32>());
@@ -1300,7 +1292,7 @@
     auto* sma = Sem().Get(mem)->As<sem::Swizzle>();
     ASSERT_NE(sma, nullptr);
     EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
-    EXPECT_THAT(sma->As<sem::Swizzle>()->Indices(), ElementsAre(0, 2, 1, 3));
+    EXPECT_THAT(sma->Indices(), ElementsAre(0, 2, 1, 3));
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_SingleElement) {
@@ -1312,14 +1304,11 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     ASSERT_NE(TypeOf(mem), nullptr);
-    ASSERT_TRUE(TypeOf(mem)->Is<type::Reference>());
-
-    auto* ref = TypeOf(mem)->As<type::Reference>();
-    ASSERT_TRUE(ref->StoreType()->Is<type::F32>());
-    auto* sma = Sem().Get(mem)->As<sem::Swizzle>();
+    ASSERT_TRUE(TypeOf(mem)->Is<type::F32>());
+    auto* sma = Sem().Get(mem)->UnwrapLoad()->As<sem::Swizzle>();
     ASSERT_NE(sma, nullptr);
     EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
-    EXPECT_THAT(Sem().Get(mem)->As<sem::Swizzle>()->Indices(), ElementsAre(2));
+    EXPECT_THAT(sma->Indices(), ElementsAre(2));
 }
 
 TEST_F(ResolverTest, Expr_Accessor_MultiLevel) {
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index 2515a02..86bc5d5 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -83,7 +83,7 @@
     /// @return the resolved sem::Variable of the identifier, or nullptr if
     /// the expression did not resolve to a variable.
     const sem::Variable* VarOf(const ast::Expression* expr) {
-        auto* sem_ident = Sem().Get(expr);
+        auto* sem_ident = Sem().Get(expr)->UnwrapLoad();
         auto* var_user = sem_ident ? sem_ident->As<sem::VariableUser>() : nullptr;
         return var_user ? var_user->Variable() : nullptr;
     }
diff --git a/src/tint/resolver/side_effects_test.cc b/src/tint/resolver/side_effects_test.cc
index 46727e2..53df777 100644
--- a/src/tint/resolver/side_effects_test.cc
+++ b/src/tint/resolver/side_effects_test.cc
@@ -83,7 +83,7 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     auto* sem = Sem().Get(expr);
     ASSERT_NE(sem, nullptr);
-    EXPECT_TRUE(sem->Is<sem::VariableUser>());
+    EXPECT_TRUE(sem->UnwrapLoad()->Is<sem::VariableUser>());
     EXPECT_FALSE(sem->HasSideEffects());
 }
 
@@ -438,8 +438,8 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     auto* sem = Sem().Get(expr);
-    EXPECT_TRUE(sem->Is<sem::MemberAccessorExpression>());
     ASSERT_NE(sem, nullptr);
+    EXPECT_TRUE(sem->UnwrapLoad()->Is<sem::MemberAccessorExpression>());
     EXPECT_FALSE(sem->HasSideEffects());
 }
 
@@ -450,8 +450,8 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     auto* sem = Sem().Get(expr);
-    EXPECT_TRUE(sem->Is<sem::Swizzle>());
     ASSERT_NE(sem, nullptr);
+    EXPECT_TRUE(sem->Is<sem::Swizzle>());
     EXPECT_FALSE(sem->HasSideEffects());
 }
 
@@ -462,8 +462,8 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     auto* sem = Sem().Get(expr);
-    EXPECT_TRUE(sem->Is<sem::Swizzle>());
     ASSERT_NE(sem, nullptr);
+    EXPECT_TRUE(sem->Is<sem::Swizzle>());
     EXPECT_TRUE(sem->HasSideEffects());
 }
 
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index db1578f..491360f 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -249,10 +249,10 @@
         // This only matters if we're dumping the graph.
         std::string unique_tag = tag;
         int suffix = 0;
-        while (tags_.count(unique_tag)) {
+        while (tags_.Contains(unique_tag)) {
             unique_tag = tag + "_$" + std::to_string(++suffix);
         }
-        tags_.insert(unique_tag);
+        tags_.Add(unique_tag);
         node->tag = name + "." + unique_tag;
 #endif
 
@@ -1035,7 +1035,7 @@
         };
 
         auto name = builder_->Symbols().NameFor(ident->symbol);
-        auto* sem = sem_.Get(ident)->UnwrapMaterialize()->As<sem::VariableUser>()->Variable();
+        auto* sem = sem_.Get(ident)->Unwrap()->As<sem::VariableUser>()->Variable();
         auto* node = CreateNode(name + "_ident_expr", ident);
         return Switch(
             sem,
@@ -1203,7 +1203,7 @@
 
             [&](const ast::IdentifierExpression* i) {
                 auto name = builder_->Symbols().NameFor(i->symbol);
-                auto* sem = sem_.Get<sem::VariableUser>(i);
+                auto* sem = sem_.Get(i)->UnwrapLoad()->As<sem::VariableUser>();
                 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>()) {
@@ -1536,7 +1536,7 @@
         Switch(
             non_uniform_source->ast,
             [&](const ast::IdentifierExpression* ident) {
-                auto* var = sem_.Get<sem::VariableUser>(ident)->Variable();
+                auto* var = sem_.Get(ident)->UnwrapLoad()->As<sem::VariableUser>()->Variable();
                 std::string var_type = get_var_type(var);
                 diagnostics_.add_note(diag::System::Resolver,
                                       "reading from " + var_type + "'" +
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 479cb9c..3172e58 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -2205,7 +2205,7 @@
 }
 
 bool Validator::SwitchStatement(const ast::SwitchStatement* s) {
-    auto* cond_ty = sem_.TypeOf(s->condition)->UnwrapRef();
+    auto* cond_ty = sem_.TypeOf(s->condition);
     if (!cond_ty->is_integer_scalar()) {
         AddError("switch statement selector expression must be of a scalar integer type",
                  s->condition->source);
diff --git a/src/tint/resolver/variable_test.cc b/src/tint/resolver/variable_test.cc
index 1961516..552ad08 100644
--- a/src/tint/resolver/variable_test.cc
+++ b/src/tint/resolver/variable_test.cc
@@ -249,7 +249,8 @@
     ASSERT_NE(local, nullptr);
     EXPECT_EQ(local->Shadows(), global);
 
-    auto* user_v = Sem().Get<sem::VariableUser>(local->Declaration()->initializer);
+    auto* user_v =
+        Sem().Get(local->Declaration()->initializer)->UnwrapLoad()->As<sem::VariableUser>();
     ASSERT_NE(user_v, nullptr);
     EXPECT_EQ(user_v->Variable(), global);
 }
@@ -298,7 +299,8 @@
     ASSERT_NE(local_y, nullptr);
     EXPECT_EQ(local_y->Shadows(), local_x);
 
-    auto* user_y = Sem().Get<sem::VariableUser>(local_y->Declaration()->initializer);
+    auto* user_y =
+        Sem().Get(local_y->Declaration()->initializer)->UnwrapLoad()->As<sem::VariableUser>();
     ASSERT_NE(user_y, nullptr);
     EXPECT_EQ(user_y->Variable(), local_x);
 }
@@ -563,7 +565,8 @@
     ASSERT_NE(local, nullptr);
     EXPECT_EQ(local->Shadows(), global);
 
-    auto* user = Sem().Get<sem::VariableUser>(local->Declaration()->initializer);
+    auto* user =
+        Sem().Get(local->Declaration()->initializer)->UnwrapLoad()->As<sem::VariableUser>();
     ASSERT_NE(user, nullptr);
     EXPECT_EQ(user->Variable(), global);
 }
@@ -612,7 +615,8 @@
     ASSERT_NE(local_l, nullptr);
     EXPECT_EQ(local_l->Shadows(), local_v);
 
-    auto* user = Sem().Get<sem::VariableUser>(local_l->Declaration()->initializer);
+    auto* user =
+        Sem().Get(local_l->Declaration()->initializer)->UnwrapLoad()->As<sem::VariableUser>();
     ASSERT_NE(user, nullptr);
     EXPECT_EQ(user->Variable(), local_v);
 }
diff --git a/src/tint/sem/array_count.cc b/src/tint/sem/array_count.cc
index 08719ad..a324ea5 100644
--- a/src/tint/sem/array_count.cc
+++ b/src/tint/sem/array_count.cc
@@ -20,14 +20,11 @@
 namespace tint::sem {
 
 NamedOverrideArrayCount::NamedOverrideArrayCount(const GlobalVariable* var)
-    : Base(), variable(var) {}
+    : Base(static_cast<size_t>(TypeInfo::Of<NamedOverrideArrayCount>().full_hashcode)),
+      variable(var) {}
 NamedOverrideArrayCount::~NamedOverrideArrayCount() = default;
 
-size_t NamedOverrideArrayCount::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<NamedOverrideArrayCount>().full_hashcode);
-}
-
-bool NamedOverrideArrayCount::Equals(const ArrayCount& other) const {
+bool NamedOverrideArrayCount::Equals(const UniqueNode& other) const {
     if (auto* v = other.As<NamedOverrideArrayCount>()) {
         return variable == v->variable;
     }
@@ -38,14 +35,11 @@
     return symbols.NameFor(variable->Declaration()->symbol);
 }
 
-UnnamedOverrideArrayCount::UnnamedOverrideArrayCount(const Expression* e) : Base(), expr(e) {}
+UnnamedOverrideArrayCount::UnnamedOverrideArrayCount(const Expression* e)
+    : Base(static_cast<size_t>(TypeInfo::Of<UnnamedOverrideArrayCount>().full_hashcode)), expr(e) {}
 UnnamedOverrideArrayCount::~UnnamedOverrideArrayCount() = default;
 
-size_t UnnamedOverrideArrayCount::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<UnnamedOverrideArrayCount>().full_hashcode);
-}
-
-bool UnnamedOverrideArrayCount::Equals(const ArrayCount& other) const {
+bool UnnamedOverrideArrayCount::Equals(const UniqueNode& other) const {
     if (auto* v = other.As<UnnamedOverrideArrayCount>()) {
         return expr == v->expr;
     }
diff --git a/src/tint/sem/array_count.h b/src/tint/sem/array_count.h
index d948106..fd441e8 100644
--- a/src/tint/sem/array_count.h
+++ b/src/tint/sem/array_count.h
@@ -36,12 +36,9 @@
     explicit NamedOverrideArrayCount(const GlobalVariable* var);
     ~NamedOverrideArrayCount() override;
 
-    /// @returns a hash of the array count.
-    size_t Hash() const override;
-
-    /// @param t other array count
-    /// @returns true if this array count is equal to the given array count
-    bool Equals(const type::ArrayCount& t) const override;
+    /// @param other the other node
+    /// @returns true if this array count is equal @p other
+    bool Equals(const type::UniqueNode& other) const override;
 
     /// @param symbols the symbol table
     /// @returns the friendly name for this array count
@@ -65,12 +62,9 @@
     explicit UnnamedOverrideArrayCount(const Expression* e);
     ~UnnamedOverrideArrayCount() override;
 
-    /// @returns a hash of the array count.
-    size_t Hash() const override;
-
-    /// @param t other array count
-    /// @returns true if this array count is equal to the given array count
-    bool Equals(const type::ArrayCount& t) const override;
+    /// @param other the other node
+    /// @returns true if this array count is equal @p other
+    bool Equals(const type::UniqueNode& other) const override;
 
     /// @param symbols the symbol table
     /// @returns the friendly name for this array count
diff --git a/src/tint/sem/expression.cc b/src/tint/sem/expression.cc
index 9d231be..cf338c4 100644
--- a/src/tint/sem/expression.cc
+++ b/src/tint/sem/expression.cc
@@ -16,6 +16,7 @@
 
 #include <utility>
 
+#include "src/tint/sem/load.h"
 #include "src/tint/sem/materialize.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::sem::Expression);
@@ -49,4 +50,19 @@
     return this;
 }
 
+const Expression* Expression::UnwrapLoad() const {
+    if (auto* l = As<Load>()) {
+        return l->Reference();
+    }
+    return this;
+}
+
+const Expression* Expression::Unwrap() const {
+    return Switch(
+        this,  // note: An expression can only be wrapped by a Load or Materialize, not both.
+        [&](const Load* load) { return load->Reference(); },
+        [&](const Materialize* materialize) { return materialize->Expr(); },
+        [&](Default) { return this; });
+}
+
 }  // namespace tint::sem
diff --git a/src/tint/sem/expression.h b/src/tint/sem/expression.h
index e39e844..575f3f5 100644
--- a/src/tint/sem/expression.h
+++ b/src/tint/sem/expression.h
@@ -85,6 +85,12 @@
     /// @return the inner expression node if this is a Materialize, otherwise this.
     const Expression* UnwrapMaterialize() const;
 
+    /// @return the inner reference expression if this is a Load, otherwise this.
+    const Expression* UnwrapLoad() const;
+
+    /// @return the inner expression node if this is a Materialize or Load, otherwise this.
+    const Expression* Unwrap() const;
+
   protected:
     /// The AST expression node for this semantic expression
     const ast::Expression* const declaration_;
diff --git a/src/tint/transform/combine_samplers.cc b/src/tint/transform/combine_samplers.cc
index 4f68388..ca42335 100644
--- a/src/tint/transform/combine_samplers.cc
+++ b/src/tint/transform/combine_samplers.cc
@@ -250,9 +250,10 @@
                     const sem::Expression* sampler =
                         sampler_index != -1 ? call->Arguments()[static_cast<size_t>(sampler_index)]
                                             : nullptr;
-                    auto* texture_var = texture->As<sem::VariableUser>()->Variable();
+                    auto* texture_var = texture->UnwrapLoad()->As<sem::VariableUser>()->Variable();
                     auto* sampler_var =
-                        sampler ? sampler->As<sem::VariableUser>()->Variable() : nullptr;
+                        sampler ? sampler->UnwrapLoad()->As<sem::VariableUser>()->Variable()
+                                : nullptr;
                     sem::VariablePair new_pair(texture_var, sampler_var);
                     for (auto* arg : expr->args) {
                         auto* type = ctx.src->TypeOf(arg)->UnwrapRef();
@@ -296,12 +297,14 @@
                         const sem::Variable* sampler_var = pair.second;
                         if (auto* param = texture_var->As<sem::Parameter>()) {
                             const sem::Expression* texture = call->Arguments()[param->Index()];
-                            texture_var = texture->As<sem::VariableUser>()->Variable();
+                            texture_var =
+                                texture->UnwrapLoad()->As<sem::VariableUser>()->Variable();
                         }
                         if (sampler_var) {
                             if (auto* param = sampler_var->As<sem::Parameter>()) {
                                 const sem::Expression* sampler = call->Arguments()[param->Index()];
-                                sampler_var = sampler->As<sem::VariableUser>()->Variable();
+                                sampler_var =
+                                    sampler->UnwrapLoad()->As<sem::VariableUser>()->Variable();
                             }
                         }
                         sem::VariablePair new_pair(texture_var, sampler_var);
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index df43788..7c96875 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -126,10 +126,10 @@
 
 /// AtomicKey is the unordered map key to an atomic intrinsic.
 struct AtomicKey {
-    ast::Access const access;           // buffer access
+    ast::Access const access;            // buffer access
     type::Type const* buf_ty = nullptr;  // buffer type
     type::Type const* el_ty = nullptr;   // element type
-    sem::BuiltinType const op;          // atomic op
+    sem::BuiltinType const op;           // atomic op
     bool operator==(const AtomicKey& rhs) const {
         return access == rhs.access && buf_ty == rhs.buf_ty && el_ty == rhs.el_ty && op == rhs.op;
     }
@@ -881,15 +881,17 @@
     for (auto* node : src->ASTNodes().Objects()) {
         if (auto* ident = node->As<ast::IdentifierExpression>()) {
             // X
-            if (auto* var = sem.Get<sem::VariableUser>(ident)) {
-                if (var->Variable()->AddressSpace() == ast::AddressSpace::kStorage ||
-                    var->Variable()->AddressSpace() == ast::AddressSpace::kUniform) {
-                    // Variable to a storage or uniform buffer
-                    state.AddAccess(ident, {
-                                               var,
-                                               state.ToOffset(0u),
-                                               var->Type()->UnwrapRef(),
-                                           });
+            if (auto* sem_ident = sem.Get(ident)) {
+                if (auto* var = sem_ident->UnwrapLoad()->As<sem::VariableUser>()) {
+                    if (var->Variable()->AddressSpace() == ast::AddressSpace::kStorage ||
+                        var->Variable()->AddressSpace() == ast::AddressSpace::kUniform) {
+                        // Variable to a storage or uniform buffer
+                        state.AddAccess(ident, {
+                                                   var,
+                                                   state.ToOffset(0u),
+                                                   var->Type()->UnwrapRef(),
+                                               });
+                    }
                 }
             }
             continue;
@@ -897,7 +899,7 @@
 
         if (auto* accessor = node->As<ast::MemberAccessorExpression>()) {
             // X.Y
-            auto* accessor_sem = sem.Get(accessor);
+            auto* accessor_sem = sem.Get(accessor)->UnwrapLoad();
             if (auto* swizzle = accessor_sem->As<sem::Swizzle>()) {
                 if (swizzle->Indices().Length() == 1) {
                     if (auto access = state.TakeAccess(accessor->structure)) {
@@ -906,7 +908,7 @@
                         state.AddAccess(accessor, {
                                                       access.var,
                                                       state.Add(access.offset, offset),
-                                                      vec_ty->type()->UnwrapRef(),
+                                                      vec_ty->type(),
                                                   });
                     }
                 }
@@ -918,7 +920,7 @@
                     state.AddAccess(accessor, {
                                                   access.var,
                                                   state.Add(access.offset, offset),
-                                                  member->Type()->UnwrapRef(),
+                                                  member->Type(),
                                               });
                 }
             }
@@ -933,7 +935,7 @@
                     state.AddAccess(accessor, {
                                                   access.var,
                                                   state.Add(access.offset, offset),
-                                                  arr->ElemType()->UnwrapRef(),
+                                                  arr->ElemType(),
                                               });
                     continue;
                 }
@@ -942,7 +944,7 @@
                     state.AddAccess(accessor, {
                                                   access.var,
                                                   state.Add(access.offset, offset),
-                                                  vec_ty->type()->UnwrapRef(),
+                                                  vec_ty->type(),
                                               });
                     continue;
                 }
@@ -1014,6 +1016,7 @@
 
     // All remaining accesses are loads, transform these into calls to the
     // corresponding load function
+    // TODO(crbug.com/tint/1784): Use `sem::Load`s instead of maintaining `state.expression_order`.
     for (auto* expr : state.expression_order) {
         auto access_it = state.accesses.find(expr);
         if (access_it == state.accesses.end()) {
diff --git a/src/tint/transform/decompose_strided_matrix.cc b/src/tint/transform/decompose_strided_matrix.cc
index a874e4e..4f2042a 100644
--- a/src/tint/transform/decompose_strided_matrix.cc
+++ b/src/tint/transform/decompose_strided_matrix.cc
@@ -167,7 +167,7 @@
     //   m = arr_to_mat(ssbo.mat)
     std::unordered_map<MatrixInfo, Symbol, MatrixInfo::Hasher> arr_to_mat;
     ctx.ReplaceAll([&](const ast::MemberAccessorExpression* expr) -> const ast::Expression* {
-        if (auto* access = src->Sem().Get<sem::StructMemberAccess>(expr)) {
+        if (auto* access = src->Sem().Get(expr)->UnwrapLoad()->As<sem::StructMemberAccess>()) {
             if (auto info = decomposed.Find(access->Member()->Declaration())) {
                 auto fn = utils::GetOrCreate(arr_to_mat, *info, [&] {
                     auto name =
diff --git a/src/tint/transform/first_index_offset.cc b/src/tint/transform/first_index_offset.cc
index eb698be..da1587d 100644
--- a/src/tint/transform/first_index_offset.cc
+++ b/src/tint/transform/first_index_offset.cc
@@ -139,7 +139,7 @@
         // Fix up all references to the builtins with the offsets
         ctx.ReplaceAll([=, &ctx](const ast::Expression* expr) -> const ast::Expression* {
             if (auto* sem = ctx.src->Sem().Get(expr)) {
-                if (auto* user = sem->As<sem::VariableUser>()) {
+                if (auto* user = sem->UnwrapLoad()->As<sem::VariableUser>()) {
                     auto it = builtin_vars.find(user->Variable());
                     if (it != builtin_vars.end()) {
                         return ctx.dst->Add(ctx.CloneWithoutTransform(expr),
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index e7d8327..f8c927b 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -197,7 +197,8 @@
             if (builtin && !builtin->Parameters().IsEmpty() &&
                 builtin->Parameters()[0]->Type()->Is<type::ExternalTexture>() &&
                 builtin->Type() != sem::BuiltinType::kTextureDimensions) {
-                if (auto* var_user = sem.Get<sem::VariableUser>(expr->args[0])) {
+                if (auto* var_user =
+                        sem.Get(expr->args[0])->UnwrapLoad()->As<sem::VariableUser>()) {
                     auto it = new_binding_symbols.find(var_user->Variable());
                     if (it == new_binding_symbols.end()) {
                         // If valid new binding locations were not provided earlier, we would have
@@ -222,7 +223,7 @@
                 // texture_external parameter. These need to be expanded out to multiple plane
                 // textures and the texture parameters structure.
                 for (auto* arg : expr->args) {
-                    if (auto* var_user = sem.Get<sem::VariableUser>(arg)) {
+                    if (auto* var_user = sem.Get(arg)->UnwrapLoad()->As<sem::VariableUser>()) {
                         // Check if a parameter is a texture_external by trying to find
                         // it in the transform state.
                         auto it = new_binding_symbols.find(var_user->Variable());
diff --git a/src/tint/transform/packed_vec3.cc b/src/tint/transform/packed_vec3.cc
index 5b5fdf4..af95161 100644
--- a/src/tint/transform/packed_vec3.cc
+++ b/src/tint/transform/packed_vec3.cc
@@ -74,8 +74,14 @@
         // that load a whole packed vector (not a scalar / swizzle of the vector).
         utils::Hashset<const sem::Expression*, 16> refs;
         for (auto* node : ctx.src->ASTNodes().Objects()) {
+            auto* sem_node = sem.Get(node);
+            if (sem_node) {
+                if (auto* expr = sem_node->As<sem::Expression>()) {
+                    sem_node = expr->UnwrapLoad();
+                }
+            }
             Switch(
-                sem.Get(node),  //
+                sem_node,  //
                 [&](const sem::StructMemberAccess* access) {
                     if (members.Contains(access->Member())) {
                         // Access to a packed vector member. Seed the expression tracking.
@@ -84,11 +90,11 @@
                 },
                 [&](const sem::IndexAccessorExpression* access) {
                     // Not loading a whole packed vector. Ignore.
-                    refs.Remove(access->Object());
+                    refs.Remove(access->Object()->UnwrapLoad());
                 },
                 [&](const sem::Swizzle* access) {
                     // Not loading a whole packed vector. Ignore.
-                    refs.Remove(access->Object());
+                    refs.Remove(access->Object()->UnwrapLoad());
                 },
                 [&](const sem::VariableUser* user) {
                     auto* v = user->Variable();
diff --git a/src/tint/transform/promote_side_effects_to_decl.cc b/src/tint/transform/promote_side_effects_to_decl.cc
index 7a5254d..ff1c18f 100644
--- a/src/tint/transform/promote_side_effects_to_decl.cc
+++ b/src/tint/transform/promote_side_effects_to_decl.cc
@@ -276,7 +276,7 @@
             },
             [&](const ast::IdentifierExpression* e) {
                 if (auto* sem_e = sem.Get(e)) {
-                    if (auto* var_user = sem_e->As<sem::VariableUser>()) {
+                    if (auto* var_user = sem_e->UnwrapLoad()->As<sem::VariableUser>()) {
                         // Don't hoist constants.
                         if (var_user->ConstantValue()) {
                             return false;
diff --git a/src/tint/transform/renamer.cc b/src/tint/transform/renamer.cc
index a84b004..8d9872d 100644
--- a/src/tint/transform/renamer.cc
+++ b/src/tint/transform/renamer.cc
@@ -1285,7 +1285,7 @@
         Switch(
             node,
             [&](const ast::MemberAccessorExpression* accessor) {
-                auto* sem = src->Sem().Get(accessor);
+                auto* sem = src->Sem().Get(accessor)->UnwrapLoad();
                 if (sem->Is<sem::Swizzle>()) {
                     preserved_identifiers.Add(accessor->member);
                 } else if (auto* str_expr = src->Sem().Get(accessor->structure)) {
diff --git a/src/tint/transform/renamer_test.cc b/src/tint/transform/renamer_test.cc
index 635bf4e..0628063 100644
--- a/src/tint/transform/renamer_test.cc
+++ b/src/tint/transform/renamer_test.cc
@@ -93,7 +93,8 @@
   var v : vec4<f32>;
   var rgba : f32;
   var xyzw : f32;
-  return v.zyxw + v.rgab;
+  var z : f32;
+  return v.zyxw + v.rgab * v.z;
 }
 )";
 
@@ -103,7 +104,8 @@
   var tint_symbol_1 : vec4<f32>;
   var tint_symbol_2 : f32;
   var tint_symbol_3 : f32;
-  return (tint_symbol_1.zyxw + tint_symbol_1.rgab);
+  var tint_symbol_4 : f32;
+  return (tint_symbol_1.zyxw + (tint_symbol_1.rgab * tint_symbol_1.z));
 }
 )";
 
@@ -115,10 +117,8 @@
 
     ASSERT_NE(data, nullptr);
     Renamer::Data::Remappings expected_remappings = {
-        {"entry", "tint_symbol"},
-        {"v", "tint_symbol_1"},
-        {"rgba", "tint_symbol_2"},
-        {"xyzw", "tint_symbol_3"},
+        {"entry", "tint_symbol"},  {"v", "tint_symbol_1"}, {"rgba", "tint_symbol_2"},
+        {"xyzw", "tint_symbol_3"}, {"z", "tint_symbol_4"},
     };
     EXPECT_THAT(data->remappings, ContainerEq(expected_remappings));
 }
diff --git a/src/tint/transform/robustness.cc b/src/tint/transform/robustness.cc
index 45b138e..1ef19c9 100644
--- a/src/tint/transform/robustness.cc
+++ b/src/tint/transform/robustness.cc
@@ -67,7 +67,7 @@
     /// @return the clamped replacement expression, or nullptr if `expr` should be cloned without
     /// changes.
     const ast::IndexAccessorExpression* Transform(const ast::IndexAccessorExpression* expr) {
-        auto* sem = src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::IndexAccessorExpression>();
+        auto* sem = src->Sem().Get(expr)->Unwrap()->As<sem::IndexAccessorExpression>();
         auto* ret_type = sem->Type();
 
         auto* ref = ret_type->As<type::Reference>();
@@ -78,7 +78,7 @@
         // idx return the cloned index expression, as a u32.
         auto idx = [&]() -> const ast::Expression* {
             auto* i = ctx.Clone(expr->index);
-            if (sem->Index()->Type()->UnwrapRef()->is_signed_integer_scalar()) {
+            if (sem->Index()->Type()->is_signed_integer_scalar()) {
                 return b.Construct(b.ty.u32(), i);  // u32(idx)
             }
             return i;
diff --git a/src/tint/transform/spirv_atomic.cc b/src/tint/transform/spirv_atomic.cc
index 6b55e5d..2471b89 100644
--- a/src/tint/transform/spirv_atomic.cc
+++ b/src/tint/transform/spirv_atomic.cc
@@ -164,7 +164,7 @@
     void ProcessAtomicExpressions() {
         for (size_t i = 0; i < atomic_expressions.Length(); i++) {
             Switch(
-                atomic_expressions[i],  //
+                atomic_expressions[i]->UnwrapLoad(),  //
                 [&](const sem::VariableUser* user) {
                     auto* v = user->Variable()->Declaration();
                     if (v->type && atomic_variables.emplace(user->Variable()).second) {
@@ -262,7 +262,7 @@
                         }
 
                         auto sem_rhs = ctx.src->Sem().Get(assign->rhs);
-                        if (is_ref_to_atomic_var(sem_rhs)) {
+                        if (is_ref_to_atomic_var(sem_rhs->UnwrapLoad())) {
                             ctx.Replace(assign->rhs, [=] {
                                 auto* rhs = ctx.CloneWithoutTransform(assign->rhs);
                                 return b.Call(sem::str(sem::BuiltinType::kAtomicLoad),
@@ -274,7 +274,7 @@
                     [&](const ast::VariableDeclStatement* decl) {
                         auto* var = decl->variable;
                         if (auto* sem_init = ctx.src->Sem().Get(var->initializer)) {
-                            if (is_ref_to_atomic_var(sem_init)) {
+                            if (is_ref_to_atomic_var(sem_init->UnwrapLoad())) {
                                 ctx.Replace(var->initializer, [=] {
                                     auto* rhs = ctx.CloneWithoutTransform(var->initializer);
                                     return b.Call(sem::str(sem::BuiltinType::kAtomicLoad),
diff --git a/src/tint/transform/std140.cc b/src/tint/transform/std140.cc
index 3f94018..0b8665a 100644
--- a/src/tint/transform/std140.cc
+++ b/src/tint/transform/std140.cc
@@ -511,7 +511,7 @@
         while (true) {
             enum class Action { kStop, kContinue, kError };
             Action action = Switch(
-                expr,  //
+                expr->Unwrap(),  //
                 [&](const sem::VariableUser* user) {
                     if (user->Variable() == access.var) {
                         // Walked all the way to the root identifier. We're done traversing.
diff --git a/src/tint/transform/unshadow.cc b/src/tint/transform/unshadow.cc
index 8d2b876..3ec5f30 100644
--- a/src/tint/transform/unshadow.cc
+++ b/src/tint/transform/unshadow.cc
@@ -96,9 +96,11 @@
         });
         ctx.ReplaceAll(
             [&](const ast::IdentifierExpression* ident) -> const tint::ast::IdentifierExpression* {
-                if (auto* user = sem.Get<sem::VariableUser>(ident)) {
-                    if (auto renamed = renamed_to.Find(user->Variable())) {
-                        return b.Expr(*renamed);
+                if (auto* sem_ident = sem.Get(ident)) {
+                    if (auto* user = sem_ident->UnwrapLoad()->As<sem::VariableUser>()) {
+                        if (auto renamed = renamed_to.Find(user->Variable())) {
+                            return b.Expr(*renamed);
+                        }
                     }
                 }
                 return nullptr;
diff --git a/src/tint/transform/unshadow_test.cc b/src/tint/transform/unshadow_test.cc
index c731e76..f9e976b 100644
--- a/src/tint/transform/unshadow_test.cc
+++ b/src/tint/transform/unshadow_test.cc
@@ -760,5 +760,31 @@
     EXPECT_EQ(expect, str(got));
 }
 
+TEST_F(UnshadowTest, RenamedVarHasUsers) {
+    auto* src = R"(
+fn F() {
+  var a : bool;
+  {
+    var a : i32;
+    var b = a + 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn F() {
+  var a : bool;
+  {
+    var a_1 : i32;
+    var b = (a_1 + 1);
+  }
+}
+)";
+
+    auto got = Run<Unshadow>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
 }  // namespace
 }  // namespace tint::transform
diff --git a/src/tint/type/abstract_float.cc b/src/tint/type/abstract_float.cc
index 7f472dc..443bb21 100644
--- a/src/tint/type/abstract_float.cc
+++ b/src/tint/type/abstract_float.cc
@@ -21,15 +21,10 @@
 
 namespace tint::type {
 
-AbstractFloat::AbstractFloat() = default;
-AbstractFloat::AbstractFloat(AbstractFloat&&) = default;
+AbstractFloat::AbstractFloat() : Base(utils::Hash(TypeInfo::Of<AbstractFloat>().full_hashcode)) {}
 AbstractFloat::~AbstractFloat() = default;
 
-size_t AbstractFloat::Hash() const {
-    return utils::Hash(TypeInfo::Of<AbstractFloat>().full_hashcode);
-}
-
-bool AbstractFloat::Equals(const Type& other) const {
+bool AbstractFloat::Equals(const UniqueNode& other) const {
     return other.Is<AbstractFloat>();
 }
 
diff --git a/src/tint/type/abstract_float.h b/src/tint/type/abstract_float.h
index 69c12be..9a7d4a8 100644
--- a/src/tint/type/abstract_float.h
+++ b/src/tint/type/abstract_float.h
@@ -28,16 +28,12 @@
     /// Constructor
     AbstractFloat();
 
-    /// Move constructor
-    AbstractFloat(AbstractFloat&&);
+    /// Destructor
     ~AbstractFloat() override;
 
-    /// @returns a hash of the type.
-    size_t Hash() const override;
-
     /// @param other the other type to compare against
     /// @returns true if this type is equal to the given type
-    bool Equals(const Type& other) const override;
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type when printed in diagnostics.
diff --git a/src/tint/type/abstract_int.cc b/src/tint/type/abstract_int.cc
index dffaab0..990d4e6 100644
--- a/src/tint/type/abstract_int.cc
+++ b/src/tint/type/abstract_int.cc
@@ -21,15 +21,11 @@
 
 namespace tint::type {
 
-AbstractInt::AbstractInt() = default;
-AbstractInt::AbstractInt(AbstractInt&&) = default;
+AbstractInt::AbstractInt() : Base(utils::Hash(TypeInfo::Of<AbstractInt>().full_hashcode)) {}
+
 AbstractInt::~AbstractInt() = default;
 
-size_t AbstractInt::Hash() const {
-    return utils::Hash(TypeInfo::Of<AbstractInt>().full_hashcode);
-}
-
-bool AbstractInt::Equals(const Type& other) const {
+bool AbstractInt::Equals(const UniqueNode& other) const {
     return other.Is<AbstractInt>();
 }
 
diff --git a/src/tint/type/abstract_int.h b/src/tint/type/abstract_int.h
index 7c7f287..2df6328 100644
--- a/src/tint/type/abstract_int.h
+++ b/src/tint/type/abstract_int.h
@@ -28,16 +28,12 @@
     /// Constructor
     AbstractInt();
 
-    /// Move constructor
-    AbstractInt(AbstractInt&&);
+    /// Destructor
     ~AbstractInt() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type when printed in diagnostics.
diff --git a/src/tint/type/abstract_numeric.cc b/src/tint/type/abstract_numeric.cc
index 390488e..838aa61 100644
--- a/src/tint/type/abstract_numeric.cc
+++ b/src/tint/type/abstract_numeric.cc
@@ -18,13 +18,13 @@
 
 namespace tint::type {
 
-AbstractNumeric::AbstractNumeric()
-    : Base(type::Flags{
-          Flag::kConstructable,
-          Flag::kCreationFixedFootprint,
-          Flag::kFixedFootprint,
-      }) {}
-AbstractNumeric::AbstractNumeric(AbstractNumeric&&) = default;
+AbstractNumeric::AbstractNumeric(size_t hash)
+    : Base(hash,
+           type::Flags{
+               Flag::kConstructable,
+               Flag::kCreationFixedFootprint,
+               Flag::kFixedFootprint,
+           }) {}
 AbstractNumeric::~AbstractNumeric() = default;
 
 uint32_t AbstractNumeric::Size() const {
diff --git a/src/tint/type/abstract_numeric.h b/src/tint/type/abstract_numeric.h
index 33cdac8..5b6b9f4 100644
--- a/src/tint/type/abstract_numeric.h
+++ b/src/tint/type/abstract_numeric.h
@@ -26,10 +26,10 @@
 class AbstractNumeric : public Castable<AbstractNumeric, Type> {
   public:
     /// Constructor
-    AbstractNumeric();
+    /// @param hash the unique hash of the node
+    explicit AbstractNumeric(size_t hash);
 
-    /// Move constructor
-    AbstractNumeric(AbstractNumeric&&);
+    /// Destructor
     ~AbstractNumeric() override;
 
     /// @returns 0, as the type is abstract.
diff --git a/src/tint/type/array.cc b/src/tint/type/array.cc
index ad903fa..6df6cdc 100644
--- a/src/tint/type/array.cc
+++ b/src/tint/type/array.cc
@@ -58,7 +58,8 @@
              uint32_t size,
              uint32_t stride,
              uint32_t implicit_stride)
-    : Base(FlagsFrom(element, count)),
+    : Base(utils::Hash(TypeInfo::Of<Array>().full_hashcode, count, align, size, stride),
+           FlagsFrom(element, count)),
       element_(element),
       count_(count),
       align_(align),
@@ -68,11 +69,7 @@
     TINT_ASSERT(Type, element_);
 }
 
-size_t Array::Hash() const {
-    return utils::Hash(TypeInfo::Of<Array>().full_hashcode, count_, align_, size_, stride_);
-}
-
-bool Array::Equals(const Type& other) const {
+bool Array::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<Array>()) {
         // Note: implicit_stride is not part of the type_name string as this is
         // derived from the element type
diff --git a/src/tint/type/array.h b/src/tint/type/array.h
index 52fb8a8..18882a4 100644
--- a/src/tint/type/array.h
+++ b/src/tint/type/array.h
@@ -52,12 +52,9 @@
           uint32_t stride,
           uint32_t implicit_stride);
 
-    /// @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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @return the array element type
     Type const* ElemType() const { return element_; }
diff --git a/src/tint/type/array_count.cc b/src/tint/type/array_count.cc
index f97e94f..bebe896 100644
--- a/src/tint/type/array_count.cc
+++ b/src/tint/type/array_count.cc
@@ -20,17 +20,14 @@
 
 namespace tint::type {
 
-ArrayCount::ArrayCount() : Base() {}
+ArrayCount::ArrayCount(size_t hash) : Base(hash) {}
 ArrayCount::~ArrayCount() = default;
 
-ConstantArrayCount::ConstantArrayCount(uint32_t val) : Base(), value(val) {}
+ConstantArrayCount::ConstantArrayCount(uint32_t val)
+    : Base(static_cast<size_t>(TypeInfo::Of<ConstantArrayCount>().full_hashcode)), value(val) {}
 ConstantArrayCount::~ConstantArrayCount() = default;
 
-size_t ConstantArrayCount::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<ConstantArrayCount>().full_hashcode);
-}
-
-bool ConstantArrayCount::Equals(const ArrayCount& other) const {
+bool ConstantArrayCount::Equals(const UniqueNode& other) const {
     if (auto* v = other.As<ConstantArrayCount>()) {
         return value == v->value;
     }
@@ -41,14 +38,11 @@
     return std::to_string(value);
 }
 
-RuntimeArrayCount::RuntimeArrayCount() : Base() {}
+RuntimeArrayCount::RuntimeArrayCount()
+    : Base(static_cast<size_t>(TypeInfo::Of<RuntimeArrayCount>().full_hashcode)) {}
 RuntimeArrayCount::~RuntimeArrayCount() = default;
 
-size_t RuntimeArrayCount::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<RuntimeArrayCount>().full_hashcode);
-}
-
-bool RuntimeArrayCount::Equals(const ArrayCount& other) const {
+bool RuntimeArrayCount::Equals(const UniqueNode& other) const {
     return other.Is<RuntimeArrayCount>();
 }
 
diff --git a/src/tint/type/array_count.h b/src/tint/type/array_count.h
index 76c1588..aa2c182 100644
--- a/src/tint/type/array_count.h
+++ b/src/tint/type/array_count.h
@@ -19,28 +19,23 @@
 #include <string>
 
 #include "src/tint/symbol_table.h"
-#include "src/tint/type/node.h"
+#include "src/tint/type/unique_node.h"
 
 namespace tint::type {
 
 /// An array count
-class ArrayCount : public Castable<ArrayCount, Node> {
+class ArrayCount : public Castable<ArrayCount, UniqueNode> {
   public:
     ~ArrayCount() override;
 
-    /// @returns a hash of the array count.
-    virtual size_t Hash() const = 0;
-
-    /// @param t other array count
-    /// @returns true if this array count is equal to the given array count
-    virtual bool Equals(const ArrayCount& t) const = 0;
-
     /// @param symbols the symbol table
     /// @returns the friendly name for this array count
     virtual std::string FriendlyName(const SymbolTable& symbols) const = 0;
 
   protected:
-    ArrayCount();
+    /// Constructor
+    /// @param hash the unique hash of the node
+    explicit ArrayCount(size_t hash);
 };
 
 /// The variant of an ArrayCount when the array is a const-expression.
@@ -56,12 +51,9 @@
     explicit ConstantArrayCount(uint32_t val);
     ~ConstantArrayCount() override;
 
-    /// @returns a hash of the array count.
-    size_t Hash() const override;
-
-    /// @param t other array count
-    /// @returns true if this array count is equal to the given array count
-    bool Equals(const ArrayCount& t) const override;
+    /// @param other the other object
+    /// @returns true if this array count is equal to other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the symbol table
     /// @returns the friendly name for this array count
@@ -82,12 +74,9 @@
     RuntimeArrayCount();
     ~RuntimeArrayCount() override;
 
-    /// @returns a hash of the array count.
-    size_t Hash() const override;
-
-    /// @param t other array count
-    /// @returns true if this array count is equal to the given array count
-    bool Equals(const ArrayCount& t) const override;
+    /// @param other the other object
+    /// @returns true if this array count is equal to other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the symbol table
     /// @returns the friendly name for this array count
@@ -96,27 +85,4 @@
 
 }  // namespace tint::type
 
-namespace std {
-
-/// std::hash specialization for tint::type::ArrayCount
-template <>
-struct hash<tint::type::ArrayCount> {
-    /// @param a the array count to obtain a hash from
-    /// @returns the hash of the array count
-    size_t operator()(const tint::type::ArrayCount& a) const { return a.Hash(); }
-};
-
-/// std::equal_to specialization for tint::type::ArrayCount
-template <>
-struct equal_to<tint::type::ArrayCount> {
-    /// @param a the first array count to compare
-    /// @param b the second array count to compare
-    /// @returns true if the two array counts are equal
-    bool operator()(const tint::type::ArrayCount& a, const tint::type::ArrayCount& b) const {
-        return a.Equals(b);
-    }
-};
-
-}  // namespace std
-
 #endif  // SRC_TINT_TYPE_ARRAY_COUNT_H_
diff --git a/src/tint/type/array_test.cc b/src/tint/type/array_test.cc
index cd4d5f1..089b12e 100644
--- a/src/tint/type/array_test.cc
+++ b/src/tint/type/array_test.cc
@@ -75,7 +75,7 @@
     auto* a = create<Array>(create<U32>(), create<ConstantArrayCount>(2u), 4u, 8u, 32u, 16u);
     auto* b = create<Array>(create<U32>(), create<ConstantArrayCount>(2u), 4u, 8u, 32u, 16u);
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(ArrayTest, Equals) {
diff --git a/src/tint/type/atomic.cc b/src/tint/type/atomic.cc
index e850874..91efcd4 100644
--- a/src/tint/type/atomic.cc
+++ b/src/tint/type/atomic.cc
@@ -23,19 +23,16 @@
 namespace tint::type {
 
 Atomic::Atomic(const type::Type* subtype)
-    : Base(type::Flags{
-          Flag::kCreationFixedFootprint,
-          Flag::kFixedFootprint,
-      }),
+    : Base(utils::Hash(TypeInfo::Of<Atomic>().full_hashcode, subtype),
+           type::Flags{
+               Flag::kCreationFixedFootprint,
+               Flag::kFixedFootprint,
+           }),
       subtype_(subtype) {
     TINT_ASSERT(AST, !subtype->Is<Reference>());
 }
 
-size_t Atomic::Hash() const {
-    return utils::Hash(TypeInfo::Of<Atomic>().full_hashcode, subtype_);
-}
-
-bool Atomic::Equals(const type::Type& other) const {
+bool Atomic::Equals(const type::UniqueNode& other) const {
     if (auto* o = other.As<Atomic>()) {
         return o->subtype_ == subtype_;
     }
@@ -56,8 +53,6 @@
     return subtype_->Align();
 }
 
-Atomic::Atomic(Atomic&&) = default;
-
 Atomic::~Atomic() = default;
 
 }  // namespace tint::type
diff --git a/src/tint/type/atomic.h b/src/tint/type/atomic.h
index c052274..31d1c5b 100644
--- a/src/tint/type/atomic.h
+++ b/src/tint/type/atomic.h
@@ -28,16 +28,12 @@
     /// @param subtype the atomic type
     explicit Atomic(const type::Type* subtype);
 
-    /// Move constructor
-    Atomic(Atomic&&);
+    /// Destructor
     ~Atomic() 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::Type& other) const override;
+    /// @param other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const type::UniqueNode& other) const override;
 
     /// @returns the atomic type
     const type::Type* Type() const { return subtype_; }
diff --git a/src/tint/type/atomic_test.cc b/src/tint/type/atomic_test.cc
index 740ce79..ed0bdec 100644
--- a/src/tint/type/atomic_test.cc
+++ b/src/tint/type/atomic_test.cc
@@ -33,7 +33,7 @@
 TEST_F(AtomicTest, Hash) {
     auto* a = create<Atomic>(create<I32>());
     auto* b = create<Atomic>(create<I32>());
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(AtomicTest, Equals) {
diff --git a/src/tint/type/bool.cc b/src/tint/type/bool.cc
index a76228d..89c55a1 100644
--- a/src/tint/type/bool.cc
+++ b/src/tint/type/bool.cc
@@ -21,21 +21,16 @@
 namespace tint::type {
 
 Bool::Bool()
-    : Base(type::Flags{
-          Flag::kConstructable,
-          Flag::kCreationFixedFootprint,
-          Flag::kFixedFootprint,
-      }) {}
-
-Bool::Bool(Bool&&) = default;
+    : Base(static_cast<size_t>(TypeInfo::Of<Bool>().full_hashcode),
+           type::Flags{
+               Flag::kConstructable,
+               Flag::kCreationFixedFootprint,
+               Flag::kFixedFootprint,
+           }) {}
 
 Bool::~Bool() = default;
 
-size_t Bool::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<Bool>().full_hashcode);
-}
-
-bool Bool::Equals(const Type& other) const {
+bool Bool::Equals(const UniqueNode& other) const {
     return other.Is<Bool>();
 }
 
diff --git a/src/tint/type/bool.h b/src/tint/type/bool.h
index 19ad8c1..a33a352 100644
--- a/src/tint/type/bool.h
+++ b/src/tint/type/bool.h
@@ -32,16 +32,13 @@
   public:
     /// Constructor
     Bool();
-    /// Move constructor
-    Bool(Bool&&);
+
+    /// Destructor
     ~Bool() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
diff --git a/src/tint/type/bool_test.cc b/src/tint/type/bool_test.cc
index 24b3014..d9b9d7a 100644
--- a/src/tint/type/bool_test.cc
+++ b/src/tint/type/bool_test.cc
@@ -29,7 +29,7 @@
 TEST_F(BoolTest, Hash) {
     auto* a = create<Bool>();
     auto* b = create<Bool>();
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(BoolTest, Equals) {
diff --git a/src/tint/type/depth_multisampled_texture.cc b/src/tint/type/depth_multisampled_texture.cc
index b6a8704..1313d70 100644
--- a/src/tint/type/depth_multisampled_texture.cc
+++ b/src/tint/type/depth_multisampled_texture.cc
@@ -28,19 +28,14 @@
 
 }  // namespace
 
-DepthMultisampledTexture::DepthMultisampledTexture(ast::TextureDimension dim) : Base(dim) {
+DepthMultisampledTexture::DepthMultisampledTexture(ast::TextureDimension dim)
+    : Base(utils::Hash(TypeInfo::Of<DepthMultisampledTexture>().full_hashcode, dim), dim) {
     TINT_ASSERT(Type, IsValidDepthDimension(dim));
 }
 
-DepthMultisampledTexture::DepthMultisampledTexture(DepthMultisampledTexture&&) = default;
-
 DepthMultisampledTexture::~DepthMultisampledTexture() = default;
 
-size_t DepthMultisampledTexture::Hash() const {
-    return utils::Hash(TypeInfo::Of<DepthMultisampledTexture>().full_hashcode, dim());
-}
-
-bool DepthMultisampledTexture::Equals(const Type& other) const {
+bool DepthMultisampledTexture::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<DepthMultisampledTexture>()) {
         return o->dim() == dim();
     }
diff --git a/src/tint/type/depth_multisampled_texture.h b/src/tint/type/depth_multisampled_texture.h
index c39a7bf..f92a00d 100644
--- a/src/tint/type/depth_multisampled_texture.h
+++ b/src/tint/type/depth_multisampled_texture.h
@@ -27,16 +27,13 @@
     /// Constructor
     /// @param dim the dimensionality of the texture
     explicit DepthMultisampledTexture(ast::TextureDimension dim);
-    /// Move constructor
-    DepthMultisampledTexture(DepthMultisampledTexture&&);
+
+    /// Destructor
     ~DepthMultisampledTexture() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
diff --git a/src/tint/type/depth_multisampled_texture_test.cc b/src/tint/type/depth_multisampled_texture_test.cc
index 1a01e54..46897e5 100644
--- a/src/tint/type/depth_multisampled_texture_test.cc
+++ b/src/tint/type/depth_multisampled_texture_test.cc
@@ -35,7 +35,7 @@
     auto* a = create<DepthMultisampledTexture>(ast::TextureDimension::k2d);
     auto* b = create<DepthMultisampledTexture>(ast::TextureDimension::k2d);
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(DepthMultisampledTextureTest, Equals) {
diff --git a/src/tint/type/depth_texture.cc b/src/tint/type/depth_texture.cc
index 34edde1..c5d5aae 100644
--- a/src/tint/type/depth_texture.cc
+++ b/src/tint/type/depth_texture.cc
@@ -29,19 +29,14 @@
 
 }  // namespace
 
-DepthTexture::DepthTexture(ast::TextureDimension dim) : Base(dim) {
+DepthTexture::DepthTexture(ast::TextureDimension dim)
+    : Base(utils::Hash(TypeInfo::Of<DepthTexture>().full_hashcode, dim), dim) {
     TINT_ASSERT(Type, IsValidDepthDimension(dim));
 }
 
-DepthTexture::DepthTexture(DepthTexture&&) = default;
-
 DepthTexture::~DepthTexture() = default;
 
-size_t DepthTexture::Hash() const {
-    return utils::Hash(TypeInfo::Of<DepthTexture>().full_hashcode, dim());
-}
-
-bool DepthTexture::Equals(const Type& other) const {
+bool DepthTexture::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<DepthTexture>()) {
         return o->dim() == dim();
     }
diff --git a/src/tint/type/depth_texture.h b/src/tint/type/depth_texture.h
index afa39f4..0e5bef3 100644
--- a/src/tint/type/depth_texture.h
+++ b/src/tint/type/depth_texture.h
@@ -27,16 +27,13 @@
     /// Constructor
     /// @param dim the dimensionality of the texture
     explicit DepthTexture(ast::TextureDimension dim);
-    /// Move constructor
-    DepthTexture(DepthTexture&&);
+
+    /// Destructor
     ~DepthTexture() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
diff --git a/src/tint/type/depth_texture_test.cc b/src/tint/type/depth_texture_test.cc
index 3b73af4..eab9ba8 100644
--- a/src/tint/type/depth_texture_test.cc
+++ b/src/tint/type/depth_texture_test.cc
@@ -37,7 +37,7 @@
     auto* a = create<DepthTexture>(ast::TextureDimension::k2d);
     auto* b = create<DepthTexture>(ast::TextureDimension::k2d);
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(DepthTextureTest, Equals) {
diff --git a/src/tint/type/external_texture.cc b/src/tint/type/external_texture.cc
index ac7e676..9bc319c 100644
--- a/src/tint/type/external_texture.cc
+++ b/src/tint/type/external_texture.cc
@@ -20,17 +20,13 @@
 
 namespace tint::type {
 
-ExternalTexture::ExternalTexture() : Base(ast::TextureDimension::k2d) {}
-
-ExternalTexture::ExternalTexture(ExternalTexture&&) = default;
+ExternalTexture::ExternalTexture()
+    : Base(static_cast<size_t>(TypeInfo::Of<ExternalTexture>().full_hashcode),
+           ast::TextureDimension::k2d) {}
 
 ExternalTexture::~ExternalTexture() = default;
 
-size_t ExternalTexture::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<ExternalTexture>().full_hashcode);
-}
-
-bool ExternalTexture::Equals(const Type& other) const {
+bool ExternalTexture::Equals(const UniqueNode& other) const {
     return other.Is<ExternalTexture>();
 }
 
diff --git a/src/tint/type/external_texture.h b/src/tint/type/external_texture.h
index 9c443c0..a60eddc 100644
--- a/src/tint/type/external_texture.h
+++ b/src/tint/type/external_texture.h
@@ -27,16 +27,12 @@
     /// Constructor
     ExternalTexture();
 
-    /// Move constructor
-    ExternalTexture(ExternalTexture&&);
+    /// Destructor
     ~ExternalTexture() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
diff --git a/src/tint/type/external_texture_test.cc b/src/tint/type/external_texture_test.cc
index 5f54015..d04b973 100644
--- a/src/tint/type/external_texture_test.cc
+++ b/src/tint/type/external_texture_test.cc
@@ -34,7 +34,7 @@
 TEST_F(ExternalTextureTest, Hash) {
     auto* a = create<ExternalTexture>();
     auto* b = create<ExternalTexture>();
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(ExternalTextureTest, Equals) {
diff --git a/src/tint/type/f16.cc b/src/tint/type/f16.cc
index 4243cb8..94849e5 100644
--- a/src/tint/type/f16.cc
+++ b/src/tint/type/f16.cc
@@ -21,21 +21,16 @@
 namespace tint::type {
 
 F16::F16()
-    : Base(type::Flags{
-          Flag::kConstructable,
-          Flag::kCreationFixedFootprint,
-          Flag::kFixedFootprint,
-      }) {}
-
-F16::F16(F16&&) = default;
+    : Base(static_cast<size_t>(TypeInfo::Of<F16>().full_hashcode),
+           type::Flags{
+               Flag::kConstructable,
+               Flag::kCreationFixedFootprint,
+               Flag::kFixedFootprint,
+           }) {}
 
 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 {
+bool F16::Equals(const UniqueNode& other) const {
     return other.Is<F16>();
 }
 
diff --git a/src/tint/type/f16.h b/src/tint/type/f16.h
index c4c3d1f..9cac21b 100644
--- a/src/tint/type/f16.h
+++ b/src/tint/type/f16.h
@@ -26,16 +26,13 @@
   public:
     /// Constructor
     F16();
-    /// Move constructor
-    F16(F16&&);
+
+    /// Destructor
     ~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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
diff --git a/src/tint/type/f16_test.cc b/src/tint/type/f16_test.cc
index 4bd0529..e486380 100644
--- a/src/tint/type/f16_test.cc
+++ b/src/tint/type/f16_test.cc
@@ -29,7 +29,7 @@
 TEST_F(F16Test, Hash) {
     auto* a = create<F16>();
     auto* b = create<F16>();
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(F16Test, Equals) {
diff --git a/src/tint/type/f32.cc b/src/tint/type/f32.cc
index ea86008..4ccd13a 100644
--- a/src/tint/type/f32.cc
+++ b/src/tint/type/f32.cc
@@ -21,21 +21,16 @@
 namespace tint::type {
 
 F32::F32()
-    : Base(type::Flags{
-          Flag::kConstructable,
-          Flag::kCreationFixedFootprint,
-          Flag::kFixedFootprint,
-      }) {}
-
-F32::F32(F32&&) = default;
+    : Base(static_cast<size_t>(TypeInfo::Of<F32>().full_hashcode),
+           type::Flags{
+               Flag::kConstructable,
+               Flag::kCreationFixedFootprint,
+               Flag::kFixedFootprint,
+           }) {}
 
 F32::~F32() = default;
 
-size_t F32::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<F32>().full_hashcode);
-}
-
-bool F32::Equals(const Type& other) const {
+bool F32::Equals(const UniqueNode& other) const {
     return other.Is<F32>();
 }
 
diff --git a/src/tint/type/f32.h b/src/tint/type/f32.h
index 57aa560..7a1e864 100644
--- a/src/tint/type/f32.h
+++ b/src/tint/type/f32.h
@@ -26,16 +26,13 @@
   public:
     /// Constructor
     F32();
-    /// Move constructor
-    F32(F32&&);
+
+    /// Destructor
     ~F32() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
diff --git a/src/tint/type/f32_test.cc b/src/tint/type/f32_test.cc
index 127a34b..8ecbdbd 100644
--- a/src/tint/type/f32_test.cc
+++ b/src/tint/type/f32_test.cc
@@ -29,7 +29,7 @@
 TEST_F(F32Test, Hash) {
     auto* a = create<F32>();
     auto* b = create<F32>();
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(F32Test, Equals) {
diff --git a/src/tint/type/i32.cc b/src/tint/type/i32.cc
index fb3d258..c616d14 100644
--- a/src/tint/type/i32.cc
+++ b/src/tint/type/i32.cc
@@ -21,21 +21,16 @@
 namespace tint::type {
 
 I32::I32()
-    : Base(type::Flags{
-          Flag::kConstructable,
-          Flag::kCreationFixedFootprint,
-          Flag::kFixedFootprint,
-      }) {}
-
-I32::I32(I32&&) = default;
+    : Base(static_cast<size_t>(TypeInfo::Of<I32>().full_hashcode),
+           type::Flags{
+               Flag::kConstructable,
+               Flag::kCreationFixedFootprint,
+               Flag::kFixedFootprint,
+           }) {}
 
 I32::~I32() = default;
 
-size_t I32::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<I32>().full_hashcode);
-}
-
-bool I32::Equals(const Type& other) const {
+bool I32::Equals(const UniqueNode& other) const {
     return other.Is<I32>();
 }
 
diff --git a/src/tint/type/i32.h b/src/tint/type/i32.h
index 9073daa..2160c3a 100644
--- a/src/tint/type/i32.h
+++ b/src/tint/type/i32.h
@@ -26,16 +26,13 @@
   public:
     /// Constructor
     I32();
-    /// Move constructor
-    I32(I32&&);
+
+    /// Destructor
     ~I32() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
diff --git a/src/tint/type/i32_test.cc b/src/tint/type/i32_test.cc
index 7b45a03..bdcbf8e 100644
--- a/src/tint/type/i32_test.cc
+++ b/src/tint/type/i32_test.cc
@@ -29,7 +29,7 @@
 TEST_F(I32Test, Hash) {
     auto* a = create<I32>();
     auto* b = create<I32>();
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(I32Test, Equals) {
diff --git a/src/tint/type/manager.h b/src/tint/type/manager.h
index 0515b14..59a8cf5 100644
--- a/src/tint/type/manager.h
+++ b/src/tint/type/manager.h
@@ -15,15 +15,10 @@
 #ifndef SRC_TINT_TYPE_MANAGER_H_
 #define SRC_TINT_TYPE_MANAGER_H_
 
-#include <functional>
-#include <string>
-#include <unordered_map>
 #include <utility>
 
-#include "src/tint/type/array_count.h"
-#include "src/tint/type/node.h"
-#include "src/tint/type/struct.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/hash.h"
 #include "src/tint/utils/unique_allocator.h"
 
 namespace tint::type {
@@ -60,19 +55,23 @@
     static Manager Wrap(const Manager& inner) {
         Manager out;
         out.types_.Wrap(inner.types_);
-        out.nodes_.Wrap(inner.nodes_);
+        out.unique_nodes_.Wrap(inner.unique_nodes_);
         return out;
     }
 
-    /// @param args the arguments used to construct the object.
+    /// @param args the arguments used to construct the type, unique node or node.
     /// @return a pointer to an instance of `T` with the provided arguments.
-    ///         If an existing instance of `T` has been constructed, then the same
-    ///         pointer is returned.
-    template <typename TYPE,
-              typename _ = std::enable_if<traits::IsTypeOrDerived<TYPE, Type>>,
-              typename... ARGS>
-    TYPE* Get(ARGS&&... args) {
-        return types_.Get<TYPE>(std::forward<ARGS>(args)...);
+    ///         If NODE derives from UniqueNode and an existing instance of `T` has been
+    ///         constructed, then the same pointer is returned.
+    template <typename NODE, typename... ARGS>
+    NODE* Get(ARGS&&... args) {
+        if constexpr (traits::IsTypeOrDerived<NODE, Type>) {
+            return types_.Get<NODE>(std::forward<ARGS>(args)...);
+        } else if constexpr (traits::IsTypeOrDerived<NODE, UniqueNode>) {
+            return unique_nodes_.Get<NODE>(std::forward<ARGS>(args)...);
+        } else {
+            return nodes_.Create<NODE>(std::forward<ARGS>(args)...);
+        }
     }
 
     /// @param args the arguments used to create the temporary used for the search.
@@ -85,68 +84,20 @@
         return types_.Find<TYPE>(std::forward<ARGS>(args)...);
     }
 
-    /// @param args the arguments used to construct the object.
-    /// @return a pointer to an instance of `T` with the provided arguments.
-    ///         If an existing instance of `T` has been constructed, then the same
-    ///         pointer is returned.
-    template <typename TYPE,
-              typename _ = std::enable_if<traits::IsTypeOrDerived<TYPE, ArrayCount> ||
-                                          traits::IsTypeOrDerived<TYPE, StructMember>>,
-              typename... ARGS>
-    TYPE* GetNode(ARGS&&... args) {
-        return nodes_.Get<TYPE>(std::forward<ARGS>(args)...);
-    }
-
     /// @returns an iterator to the beginning of the types
     TypeIterator begin() const { return types_.begin(); }
     /// @returns an iterator to the end of the types
     TypeIterator end() const { return types_.end(); }
 
   private:
+    /// Unique types owned by the manager
     utils::UniqueAllocator<Type> types_;
-    utils::UniqueAllocator<Node> nodes_;
+    /// Unique nodes (excluding types) owned by the manager
+    utils::UniqueAllocator<UniqueNode> unique_nodes_;
+    /// Non-unique nodes owned by the manager
+    utils::BlockAllocator<Node> nodes_;
 };
 
 }  // namespace tint::type
 
-namespace std {
-
-/// std::hash specialization for tint::type::Node
-template <>
-struct hash<tint::type::Node> {
-    /// @param type the type to obtain a hash from
-    /// @returns the hash of the type
-    size_t operator()(const tint::type::Node& type) const {
-        if (const auto* ac = type.As<tint::type::ArrayCount>()) {
-            return ac->Hash();
-        } else if (type.Is<tint::type::StructMember>()) {
-            return tint::TypeInfo::Of<tint::type::StructMember>().full_hashcode;
-        }
-        TINT_ASSERT(Type, false && "Unreachable");
-        return 0;
-    }
-};
-
-/// std::equal_to specialization for tint::type::Node
-template <>
-struct equal_to<tint::type::Node> {
-    /// @param a the first type to compare
-    /// @param b the second type to compare
-    /// @returns true if the two types are equal
-    bool operator()(const tint::type::Node& a, const tint::type::Node& b) const {
-        if (const auto* ac = a.As<tint::type::ArrayCount>()) {
-            if (const auto* bc = b.As<tint::type::ArrayCount>()) {
-                return ac->Equals(*bc);
-            }
-            return false;
-        } else if (a.Is<tint::type::StructMember>()) {
-            return &a == &b;
-        }
-        TINT_ASSERT(Type, false && "Unreachable");
-        return false;
-    }
-};
-
-}  // namespace std
-
 #endif  // SRC_TINT_TYPE_MANAGER_H_
diff --git a/src/tint/type/matrix.cc b/src/tint/type/matrix.cc
index 063ac88..0d7571f 100644
--- a/src/tint/type/matrix.cc
+++ b/src/tint/type/matrix.cc
@@ -23,11 +23,12 @@
 namespace tint::type {
 
 Matrix::Matrix(const Vector* column_type, uint32_t columns)
-    : Base(type::Flags{
-          Flag::kConstructable,
-          Flag::kCreationFixedFootprint,
-          Flag::kFixedFootprint,
-      }),
+    : Base(utils::Hash(TypeInfo::Of<Vector>().full_hashcode, columns, column_type),
+           type::Flags{
+               Flag::kConstructable,
+               Flag::kCreationFixedFootprint,
+               Flag::kFixedFootprint,
+           }),
       subtype_(column_type->type()),
       column_type_(column_type),
       rows_(column_type->Width()),
@@ -38,15 +39,9 @@
     TINT_ASSERT(AST, columns_ < 5);
 }
 
-Matrix::Matrix(Matrix&&) = default;
-
 Matrix::~Matrix() = default;
 
-size_t Matrix::Hash() const {
-    return utils::Hash(TypeInfo::Of<Vector>().full_hashcode, rows_, columns_, column_type_);
-}
-
-bool Matrix::Equals(const Type& other) const {
+bool Matrix::Equals(const UniqueNode& other) const {
     if (auto* v = other.As<Matrix>()) {
         return v->rows_ == rows_ && v->columns_ == columns_ && v->column_type_ == column_type_;
     }
diff --git a/src/tint/type/matrix.h b/src/tint/type/matrix.h
index 3191f95..70555ea 100644
--- a/src/tint/type/matrix.h
+++ b/src/tint/type/matrix.h
@@ -33,16 +33,13 @@
     /// @param column_type the type of a column of the matrix
     /// @param columns the number of columns in the matrix
     Matrix(const Vector* column_type, uint32_t columns);
-    /// Move constructor
-    Matrix(Matrix&&);
+
+    /// Destructor
     ~Matrix() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @returns the type of the matrix
     const Type* type() const { return subtype_; }
diff --git a/src/tint/type/matrix_test.cc b/src/tint/type/matrix_test.cc
index 7d88c6a..3a74741 100644
--- a/src/tint/type/matrix_test.cc
+++ b/src/tint/type/matrix_test.cc
@@ -41,7 +41,7 @@
     auto* a = create<Matrix>(create<Vector>(create<I32>(), 3u), 4u);
     auto* b = create<Matrix>(create<Vector>(create<I32>(), 3u), 4u);
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(MatrixTest, Equals) {
diff --git a/src/tint/type/multisampled_texture.cc b/src/tint/type/multisampled_texture.cc
index e1d2ed7..16588f4 100644
--- a/src/tint/type/multisampled_texture.cc
+++ b/src/tint/type/multisampled_texture.cc
@@ -22,19 +22,14 @@
 namespace tint::type {
 
 MultisampledTexture::MultisampledTexture(ast::TextureDimension dim, const Type* type)
-    : Base(dim), type_(type) {
+    : Base(utils::Hash(TypeInfo::Of<MultisampledTexture>().full_hashcode, dim, type), dim),
+      type_(type) {
     TINT_ASSERT(Type, type_);
 }
 
-MultisampledTexture::MultisampledTexture(MultisampledTexture&&) = default;
-
 MultisampledTexture::~MultisampledTexture() = default;
 
-size_t MultisampledTexture::Hash() const {
-    return utils::Hash(TypeInfo::Of<MultisampledTexture>().full_hashcode, dim(), type_);
-}
-
-bool MultisampledTexture::Equals(const Type& other) const {
+bool MultisampledTexture::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<MultisampledTexture>()) {
         return o->dim() == dim() && o->type_ == type_;
     }
diff --git a/src/tint/type/multisampled_texture.h b/src/tint/type/multisampled_texture.h
index 421b294..f78d40b 100644
--- a/src/tint/type/multisampled_texture.h
+++ b/src/tint/type/multisampled_texture.h
@@ -28,16 +28,13 @@
     /// @param dim the dimensionality of the texture
     /// @param type the data type of the multisampled texture
     MultisampledTexture(ast::TextureDimension dim, const Type* type);
-    /// Move constructor
-    MultisampledTexture(MultisampledTexture&&);
+
+    /// Destructor
     ~MultisampledTexture() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @returns the subtype of the sampled texture
     const Type* type() const { return type_; }
diff --git a/src/tint/type/multisampled_texture_test.cc b/src/tint/type/multisampled_texture_test.cc
index 8eac7c6..e919ccf 100644
--- a/src/tint/type/multisampled_texture_test.cc
+++ b/src/tint/type/multisampled_texture_test.cc
@@ -38,7 +38,7 @@
 TEST_F(MultisampledTextureTest, Hash) {
     auto* a = create<MultisampledTexture>(ast::TextureDimension::k2d, create<F32>());
     auto* b = create<MultisampledTexture>(ast::TextureDimension::k2d, create<F32>());
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(MultisampledTextureTest, Equals) {
diff --git a/src/tint/type/pointer.cc b/src/tint/type/pointer.cc
index c59170c..0ae8e0a 100644
--- a/src/tint/type/pointer.cc
+++ b/src/tint/type/pointer.cc
@@ -23,16 +23,16 @@
 namespace tint::type {
 
 Pointer::Pointer(const Type* subtype, ast::AddressSpace address_space, ast::Access access)
-    : Base(type::Flags{}), subtype_(subtype), address_space_(address_space), access_(access) {
+    : Base(utils::Hash(TypeInfo::Of<Pointer>().full_hashcode, address_space, subtype, access),
+           type::Flags{}),
+      subtype_(subtype),
+      address_space_(address_space),
+      access_(access) {
     TINT_ASSERT(Type, !subtype->Is<Reference>());
     TINT_ASSERT(Type, access != ast::Access::kUndefined);
 }
 
-size_t Pointer::Hash() const {
-    return utils::Hash(TypeInfo::Of<Pointer>().full_hashcode, address_space_, subtype_, access_);
-}
-
-bool Pointer::Equals(const Type& other) const {
+bool Pointer::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<Pointer>()) {
         return o->address_space_ == address_space_ && o->subtype_ == subtype_ &&
                o->access_ == access_;
@@ -51,8 +51,6 @@
     return out.str();
 }
 
-Pointer::Pointer(Pointer&&) = default;
-
 Pointer::~Pointer() = default;
 
 }  // namespace tint::type
diff --git a/src/tint/type/pointer.h b/src/tint/type/pointer.h
index a7703e4..3f7ff01 100644
--- a/src/tint/type/pointer.h
+++ b/src/tint/type/pointer.h
@@ -32,16 +32,12 @@
     /// @param access the resolved access control of the reference
     Pointer(const Type* subtype, ast::AddressSpace address_space, ast::Access access);
 
-    /// Move constructor
-    Pointer(Pointer&&);
+    /// Destructor
     ~Pointer() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @returns the pointee type
     const Type* StoreType() const { return subtype_; }
diff --git a/src/tint/type/pointer_test.cc b/src/tint/type/pointer_test.cc
index ab51f7c..af3587a 100644
--- a/src/tint/type/pointer_test.cc
+++ b/src/tint/type/pointer_test.cc
@@ -41,7 +41,7 @@
     auto* a = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* b = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(PointerTest, Equals) {
diff --git a/src/tint/type/reference.cc b/src/tint/type/reference.cc
index 0541f84..f811b28 100644
--- a/src/tint/type/reference.cc
+++ b/src/tint/type/reference.cc
@@ -22,16 +22,16 @@
 namespace tint::type {
 
 Reference::Reference(const Type* subtype, ast::AddressSpace address_space, ast::Access access)
-    : Base(type::Flags{}), subtype_(subtype), address_space_(address_space), access_(access) {
+    : Base(utils::Hash(TypeInfo::Of<Reference>().full_hashcode, address_space, subtype, access),
+           type::Flags{}),
+      subtype_(subtype),
+      address_space_(address_space),
+      access_(access) {
     TINT_ASSERT(Type, !subtype->Is<Reference>());
     TINT_ASSERT(Type, access != ast::Access::kUndefined);
 }
 
-size_t Reference::Hash() const {
-    return utils::Hash(TypeInfo::Of<Reference>().full_hashcode, address_space_, subtype_, access_);
-}
-
-bool Reference::Equals(const Type& other) const {
+bool Reference::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<Reference>()) {
         return o->address_space_ == address_space_ && o->subtype_ == subtype_ &&
                o->access_ == access_;
@@ -50,8 +50,6 @@
     return out.str();
 }
 
-Reference::Reference(Reference&&) = default;
-
 Reference::~Reference() = default;
 
 }  // namespace tint::type
diff --git a/src/tint/type/reference.h b/src/tint/type/reference.h
index ebf307c..e13b3cf 100644
--- a/src/tint/type/reference.h
+++ b/src/tint/type/reference.h
@@ -32,16 +32,12 @@
     /// @param access the resolved access control of the reference
     Reference(const Type* subtype, ast::AddressSpace address_space, ast::Access access);
 
-    /// Move constructor
-    Reference(Reference&&);
+    /// Destructor
     ~Reference() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @returns the pointee type
     const Type* StoreType() const { return subtype_; }
diff --git a/src/tint/type/reference_test.cc b/src/tint/type/reference_test.cc
index 094770e..e9f6128 100644
--- a/src/tint/type/reference_test.cc
+++ b/src/tint/type/reference_test.cc
@@ -47,7 +47,7 @@
     auto* b =
         create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(ReferenceTest, Equals) {
diff --git a/src/tint/type/sampled_texture.cc b/src/tint/type/sampled_texture.cc
index 2a708e5..a8bdce6 100644
--- a/src/tint/type/sampled_texture.cc
+++ b/src/tint/type/sampled_texture.cc
@@ -22,19 +22,13 @@
 namespace tint::type {
 
 SampledTexture::SampledTexture(ast::TextureDimension dim, const Type* type)
-    : Base(dim), type_(type) {
+    : Base(utils::Hash(TypeInfo::Of<SampledTexture>().full_hashcode, dim, type), dim), type_(type) {
     TINT_ASSERT(Type, type_);
 }
 
-SampledTexture::SampledTexture(SampledTexture&&) = default;
-
 SampledTexture::~SampledTexture() = default;
 
-size_t SampledTexture::Hash() const {
-    return utils::Hash(TypeInfo::Of<SampledTexture>().full_hashcode, dim(), type_);
-}
-
-bool SampledTexture::Equals(const Type& other) const {
+bool SampledTexture::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<SampledTexture>()) {
         return o->dim() == dim() && o->type_ == type_;
     }
diff --git a/src/tint/type/sampled_texture.h b/src/tint/type/sampled_texture.h
index 2e82f28..5272102 100644
--- a/src/tint/type/sampled_texture.h
+++ b/src/tint/type/sampled_texture.h
@@ -28,16 +28,13 @@
     /// @param dim the dimensionality of the texture
     /// @param type the data type of the sampled texture
     SampledTexture(ast::TextureDimension dim, const Type* type);
-    /// Move constructor
-    SampledTexture(SampledTexture&&);
+
+    /// Destructor
     ~SampledTexture() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @returns the subtype of the sampled texture
     Type* type() const { return const_cast<Type*>(type_); }
diff --git a/src/tint/type/sampled_texture_test.cc b/src/tint/type/sampled_texture_test.cc
index f9cb5be..772629f 100644
--- a/src/tint/type/sampled_texture_test.cc
+++ b/src/tint/type/sampled_texture_test.cc
@@ -42,7 +42,7 @@
     auto* a = create<SampledTexture>(ast::TextureDimension::kCube, create<F32>());
     auto* b = create<SampledTexture>(ast::TextureDimension::kCube, create<F32>());
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(SampledTextureTest, Equals) {
diff --git a/src/tint/type/sampler.cc b/src/tint/type/sampler.cc
index c49a8ef..9de1ea8 100644
--- a/src/tint/type/sampler.cc
+++ b/src/tint/type/sampler.cc
@@ -21,17 +21,12 @@
 
 namespace tint::type {
 
-Sampler::Sampler(ast::SamplerKind kind) : Base(type::Flags{}), kind_(kind) {}
-
-Sampler::Sampler(Sampler&&) = default;
+Sampler::Sampler(ast::SamplerKind kind)
+    : Base(utils::Hash(TypeInfo::Of<Sampler>().full_hashcode, kind), type::Flags{}), kind_(kind) {}
 
 Sampler::~Sampler() = default;
 
-size_t Sampler::Hash() const {
-    return utils::Hash(TypeInfo::Of<Sampler>().full_hashcode, kind_);
-}
-
-bool Sampler::Equals(const Type& other) const {
+bool Sampler::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<Sampler>()) {
         return o->kind_ == kind_;
     }
diff --git a/src/tint/type/sampler.h b/src/tint/type/sampler.h
index c8ff9d6..7778dcd 100644
--- a/src/tint/type/sampler.h
+++ b/src/tint/type/sampler.h
@@ -28,16 +28,13 @@
     /// Constructor
     /// @param kind the kind of sampler
     explicit Sampler(ast::SamplerKind kind);
-    /// Move constructor
-    Sampler(Sampler&&);
+
+    /// Destructor
     ~Sampler() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @returns the sampler type
     ast::SamplerKind kind() const { return kind_; }
diff --git a/src/tint/type/sampler_test.cc b/src/tint/type/sampler_test.cc
index ba30541..262dd18 100644
--- a/src/tint/type/sampler_test.cc
+++ b/src/tint/type/sampler_test.cc
@@ -40,7 +40,7 @@
     auto* a = create<Sampler>(ast::SamplerKind::kSampler);
     auto* b = create<Sampler>(ast::SamplerKind::kSampler);
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(SamplerTest, Equals) {
diff --git a/src/tint/type/storage_texture.cc b/src/tint/type/storage_texture.cc
index 2bd2c28..ae574f1 100644
--- a/src/tint/type/storage_texture.cc
+++ b/src/tint/type/storage_texture.cc
@@ -25,17 +25,14 @@
                                ast::TexelFormat format,
                                ast::Access access,
                                Type* subtype)
-    : Base(dim), texel_format_(format), access_(access), subtype_(subtype) {}
-
-StorageTexture::StorageTexture(StorageTexture&&) = default;
+    : Base(utils::Hash(TypeInfo::Of<StorageTexture>().full_hashcode, dim, format, access), dim),
+      texel_format_(format),
+      access_(access),
+      subtype_(subtype) {}
 
 StorageTexture::~StorageTexture() = default;
 
-size_t StorageTexture::Hash() const {
-    return utils::Hash(TypeInfo::Of<StorageTexture>().full_hashcode, dim(), texel_format_, access_);
-}
-
-bool StorageTexture::Equals(const Type& other) const {
+bool StorageTexture::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<StorageTexture>()) {
         return o->dim() == dim() && o->texel_format_ == texel_format_ && o->access_ == access_;
     }
diff --git a/src/tint/type/storage_texture.h b/src/tint/type/storage_texture.h
index 0e59775..25b452b 100644
--- a/src/tint/type/storage_texture.h
+++ b/src/tint/type/storage_texture.h
@@ -41,16 +41,12 @@
                    ast::Access access,
                    Type* subtype);
 
-    /// Move constructor
-    StorageTexture(StorageTexture&&);
+    /// Destructor
     ~StorageTexture() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @returns the storage subtype
     Type* type() const { return subtype_; }
diff --git a/src/tint/type/storage_texture_test.cc b/src/tint/type/storage_texture_test.cc
index 0c86d89..30293ee 100644
--- a/src/tint/type/storage_texture_test.cc
+++ b/src/tint/type/storage_texture_test.cc
@@ -56,7 +56,7 @@
     auto* b = Create(ast::TextureDimension::kCube, ast::TexelFormat::kRgba32Float,
                      ast::Access::kReadWrite);
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(StorageTextureTest, Equals) {
diff --git a/src/tint/type/struct.cc b/src/tint/type/struct.cc
index 778a600..f54f21f 100644
--- a/src/tint/type/struct.cc
+++ b/src/tint/type/struct.cc
@@ -56,7 +56,7 @@
                uint32_t align,
                uint32_t size,
                uint32_t size_no_padding)
-    : Base(FlagsFrom(members)),
+    : Base(utils::Hash(TypeInfo::Of<Struct>().full_hashcode, name), FlagsFrom(members)),
       source_(source),
       name_(name),
       members_(std::move(members)),
@@ -66,11 +66,7 @@
 
 Struct::~Struct() = default;
 
-size_t Struct::Hash() const {
-    return utils::Hash(TypeInfo::Of<Struct>().full_hashcode, name_);
-}
-
-bool Struct::Equals(const Type& other) const {
+bool Struct::Equals(const UniqueNode& other) const {
     if (auto* o = other.As<Struct>()) {
         return o->name_ == name_;
     }
diff --git a/src/tint/type/struct.h b/src/tint/type/struct.h
index 8946714..ff23a89 100644
--- a/src/tint/type/struct.h
+++ b/src/tint/type/struct.h
@@ -65,12 +65,9 @@
     /// Destructor
     ~Struct() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @returns the source of the structure
     tint::Source Source() const { return source_; }
diff --git a/src/tint/type/texture.cc b/src/tint/type/texture.cc
index 0aa8549..57ce265 100644
--- a/src/tint/type/texture.cc
+++ b/src/tint/type/texture.cc
@@ -18,9 +18,7 @@
 
 namespace tint::type {
 
-Texture::Texture(ast::TextureDimension dim) : Base(type::Flags{}), dim_(dim) {}
-
-Texture::Texture(Texture&&) = default;
+Texture::Texture(size_t hash, ast::TextureDimension dim) : Base(hash, type::Flags{}), dim_(dim) {}
 
 Texture::~Texture() = default;
 
diff --git a/src/tint/type/texture.h b/src/tint/type/texture.h
index 4ce0120..fc0f72d 100644
--- a/src/tint/type/texture.h
+++ b/src/tint/type/texture.h
@@ -24,10 +24,10 @@
 class Texture : public Castable<Texture, Type> {
   public:
     /// Constructor
+    /// @param hash the unique hash of the node
     /// @param dim the dimensionality of the texture
-    explicit Texture(ast::TextureDimension dim);
-    /// Move constructor
-    Texture(Texture&&);
+    Texture(size_t hash, ast::TextureDimension dim);
+    /// Destructor
     ~Texture() override;
 
     /// @returns the texture dimension
diff --git a/src/tint/type/type.cc b/src/tint/type/type.cc
index d7bccc0..6163a9a 100644
--- a/src/tint/type/type.cc
+++ b/src/tint/type/type.cc
@@ -34,14 +34,12 @@
 
 namespace tint::type {
 
-Type::Type(type::Flags flags) : flags_(flags) {
+Type::Type(size_t hash, type::Flags flags) : Base(hash), flags_(flags) {
     if (IsConstructible()) {
         TINT_ASSERT(Type, HasCreationFixedFootprint());
     }
 }
 
-Type::Type(Type&&) = default;
-
 Type::~Type() = default;
 
 const Type* Type::UnwrapPtr() const {
diff --git a/src/tint/type/type.h b/src/tint/type/type.h
index 00e1752..ccd0fe5 100644
--- a/src/tint/type/type.h
+++ b/src/tint/type/type.h
@@ -18,7 +18,7 @@
 #include <functional>
 #include <string>
 
-#include "src/tint/type/node.h"
+#include "src/tint/type/unique_node.h"
 #include "src/tint/utils/enum_set.h"
 #include "src/tint/utils/vector.h"
 
@@ -46,18 +46,11 @@
 using Flags = utils::EnumSet<Flag>;
 
 /// Base class for a type in the system
-class Type : public Castable<Type, Node> {
+class Type : public Castable<Type, UniqueNode> {
   public:
-    /// Move constructor
-    Type(Type&&);
+    /// Destructor
     ~Type() override;
 
-    /// @returns a hash of the type.
-    virtual size_t Hash() const = 0;
-
-    /// @returns true if the this type is equal to the given type
-    virtual bool Equals(const Type&) const = 0;
-
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
     /// declared in WGSL.
@@ -193,8 +186,9 @@
 
   protected:
     /// Constructor
+    /// @param hash the immutable hash for the node
     /// @param flags the flags of this type
-    explicit Type(type::Flags flags);
+    Type(size_t hash, type::Flags flags);
 
     /// The flags of this type.
     const type::Flags flags_;
@@ -209,7 +203,7 @@
 struct hash<tint::type::Type> {
     /// @param type the type to obtain a hash from
     /// @returns the hash of the type
-    size_t operator()(const tint::type::Type& type) const { return type.Hash(); }
+    size_t operator()(const tint::type::Type& type) const { return type.unique_hash; }
 };
 
 /// std::equal_to specialization for tint::type::Type
diff --git a/src/tint/type/u32.cc b/src/tint/type/u32.cc
index fd38cb9..34ae027 100644
--- a/src/tint/type/u32.cc
+++ b/src/tint/type/u32.cc
@@ -21,21 +21,16 @@
 namespace tint::type {
 
 U32::U32()
-    : Base(type::Flags{
-          Flag::kConstructable,
-          Flag::kCreationFixedFootprint,
-          Flag::kFixedFootprint,
-      }) {}
+    : Base(static_cast<size_t>(TypeInfo::Of<U32>().full_hashcode),
+           type::Flags{
+               Flag::kConstructable,
+               Flag::kCreationFixedFootprint,
+               Flag::kFixedFootprint,
+           }) {}
 
 U32::~U32() = default;
 
-U32::U32(U32&&) = default;
-
-size_t U32::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<U32>().full_hashcode);
-}
-
-bool U32::Equals(const Type& other) const {
+bool U32::Equals(const UniqueNode& other) const {
     return other.Is<U32>();
 }
 
diff --git a/src/tint/type/u32.h b/src/tint/type/u32.h
index 6e2c4cc..222a387 100644
--- a/src/tint/type/u32.h
+++ b/src/tint/type/u32.h
@@ -26,16 +26,13 @@
   public:
     /// Constructor
     U32();
-    /// Move constructor
-    U32(U32&&);
+
+    /// Destructor
     ~U32() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
diff --git a/src/tint/type/u32_test.cc b/src/tint/type/u32_test.cc
index 784b6d6..5ec5502 100644
--- a/src/tint/type/u32_test.cc
+++ b/src/tint/type/u32_test.cc
@@ -29,7 +29,7 @@
 TEST_F(U32Test, Hash) {
     auto* a = create<U32>();
     auto* b = create<U32>();
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(U32Test, Equals) {
diff --git a/src/tint/type/unique_node.cc b/src/tint/type/unique_node.cc
new file mode 100644
index 0000000..280c98b
--- /dev/null
+++ b/src/tint/type/unique_node.cc
@@ -0,0 +1,23 @@
+// 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/type/unique_node.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::type::UniqueNode);
+
+namespace tint::type {
+
+UniqueNode::~UniqueNode() = default;
+
+}  // namespace tint::type
diff --git a/src/tint/type/unique_node.h b/src/tint/type/unique_node.h
new file mode 100644
index 0000000..1fd6011
--- /dev/null
+++ b/src/tint/type/unique_node.h
@@ -0,0 +1,70 @@
+// 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_TYPE_UNIQUE_NODE_H_
+#define SRC_TINT_TYPE_UNIQUE_NODE_H_
+
+#include <functional>
+
+#include "src/tint/type/node.h"
+
+namespace tint::type {
+
+/// UniqueNode is the base class for objects that are de-duplicated by the Manager.
+/// Deduplication is achieved by comparing a temporary object to the set of existing objects, using
+/// Hash() and Equals(). If an existing object is found, then the pointer to that object is
+/// returned, otherwise a new object is constructed, added to the Manager's set and returned.
+class UniqueNode : public Castable<UniqueNode, Node> {
+  public:
+    /// Constructor
+    /// @param hash the immutable hash for the node
+    inline explicit UniqueNode(size_t hash) : unique_hash(hash) {}
+
+    /// Destructor
+    ~UniqueNode() override;
+
+    /// @param other the other node to compare this node against
+    /// @returns true if the this node is equal to @p other
+    virtual bool Equals(const UniqueNode& other) const = 0;
+
+    /// the immutable hash for the node
+    const size_t unique_hash;
+};
+
+}  // namespace tint::type
+
+namespace std {
+
+/// std::hash specialization for tint::type::UniqueNode
+template <>
+struct hash<tint::type::UniqueNode> {
+    /// @param node the unique node to obtain a hash from
+    /// @returns the hash of the node
+    size_t operator()(const tint::type::UniqueNode& node) const { return node.unique_hash; }
+};
+
+/// std::equal_to specialization for tint::type::UniqueNode
+template <>
+struct equal_to<tint::type::UniqueNode> {
+    /// @param a the first unique node to compare
+    /// @param b the second unique node to compare
+    /// @returns true if the two nodes are equal
+    bool operator()(const tint::type::UniqueNode& a, const tint::type::UniqueNode& b) const {
+        return &a == &b || a.Equals(b);
+    }
+};
+
+}  // namespace std
+
+#endif  // SRC_TINT_TYPE_UNIQUE_NODE_H_
diff --git a/src/tint/type/vector.cc b/src/tint/type/vector.cc
index 70b33bc..a1f964e 100644
--- a/src/tint/type/vector.cc
+++ b/src/tint/type/vector.cc
@@ -22,26 +22,21 @@
 namespace tint::type {
 
 Vector::Vector(Type const* subtype, uint32_t width)
-    : Base(type::Flags{
-          Flag::kConstructable,
-          Flag::kCreationFixedFootprint,
-          Flag::kFixedFootprint,
-      }),
+    : Base(utils::Hash(TypeInfo::Of<Vector>().full_hashcode, width, subtype),
+           type::Flags{
+               Flag::kConstructable,
+               Flag::kCreationFixedFootprint,
+               Flag::kFixedFootprint,
+           }),
       subtype_(subtype),
       width_(width) {
     TINT_ASSERT(Type, width_ > 1);
     TINT_ASSERT(Type, width_ < 5);
 }
 
-Vector::Vector(Vector&&) = default;
-
 Vector::~Vector() = default;
 
-size_t Vector::Hash() const {
-    return utils::Hash(TypeInfo::Of<Vector>().full_hashcode, width_, subtype_);
-}
-
-bool Vector::Equals(const Type& other) const {
+bool Vector::Equals(const UniqueNode& other) const {
     if (auto* v = other.As<Vector>()) {
         return v->width_ == width_ && v->subtype_ == subtype_;
     }
diff --git a/src/tint/type/vector.h b/src/tint/type/vector.h
index fb5834d..86f6f95 100644
--- a/src/tint/type/vector.h
+++ b/src/tint/type/vector.h
@@ -28,16 +28,13 @@
     /// @param subtype the vector element type
     /// @param size the number of elements in the vector
     Vector(Type const* subtype, uint32_t size);
-    /// Move constructor
-    Vector(Vector&&);
+
+    /// Destructor
     ~Vector() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @returns the type of the vector elements
     const Type* type() const { return subtype_; }
diff --git a/src/tint/type/vector_test.cc b/src/tint/type/vector_test.cc
index 65d7b1b..2bd144e 100644
--- a/src/tint/type/vector_test.cc
+++ b/src/tint/type/vector_test.cc
@@ -38,7 +38,7 @@
     auto* a = create<Vector>(create<I32>(), 2u);
     auto* b = create<Vector>(create<I32>(), 2u);
 
-    EXPECT_EQ(a->Hash(), b->Hash());
+    EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(VectorTest, Equals) {
diff --git a/src/tint/type/void.cc b/src/tint/type/void.cc
index b993dfe..3522607 100644
--- a/src/tint/type/void.cc
+++ b/src/tint/type/void.cc
@@ -20,17 +20,11 @@
 
 namespace tint::type {
 
-Void::Void() : Base(type::Flags{}) {}
-
-Void::Void(Void&&) = default;
+Void::Void() : Base(static_cast<size_t>(TypeInfo::Of<Void>().full_hashcode), type::Flags{}) {}
 
 Void::~Void() = default;
 
-size_t Void::Hash() const {
-    return static_cast<size_t>(TypeInfo::Of<Void>().full_hashcode);
-}
-
-bool Void::Equals(const Type& other) const {
+bool Void::Equals(const UniqueNode& other) const {
     return other.Is<Void>();
 }
 
diff --git a/src/tint/type/void.h b/src/tint/type/void.h
index 2106aea..801a671 100644
--- a/src/tint/type/void.h
+++ b/src/tint/type/void.h
@@ -26,16 +26,13 @@
   public:
     /// Constructor
     Void();
-    /// Move constructor
-    Void(Void&&);
+
+    /// Destructor
     ~Void() 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 other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 2da5c8c..41705cf 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -2706,7 +2706,7 @@
     }
     out << ".";
 
-    auto* sem = builder_.Sem().Get(expr);
+    auto* sem = builder_.Sem().Get(expr)->UnwrapLoad();
 
     return Switch(
         sem,
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index ece3723..93589e5 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -3724,7 +3724,7 @@
     }
     out << ".";
 
-    auto* sem = builder_.Sem().Get(expr);
+    auto* sem = builder_.Sem().Get(expr)->UnwrapLoad();
 
     return Switch(
         sem,
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index c3fbe4e..ab18316 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -2339,7 +2339,7 @@
         return true;
     };
 
-    auto* sem = builder_.Sem().Get(expr);
+    auto* sem = builder_.Sem().Get(expr)->UnwrapLoad();
 
     return Switch(
         sem,
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index dab6869..9d1092e 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -26,6 +26,7 @@
 #include "src/tint/sem/builtin.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/function.h"
+#include "src/tint/sem/load.h"
 #include "src/tint/sem/materialize.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/module.h"
@@ -436,7 +437,7 @@
         if (lhs_id == 0) {
             return false;
         }
-        auto rhs_id = GenerateExpressionWithLoadIfNeeded(assign->rhs);
+        auto rhs_id = GenerateExpression(assign->rhs);
         if (rhs_id == 0) {
             return false;
         }
@@ -457,7 +458,7 @@
 
 bool Builder::GenerateBreakIfStatement(const ast::BreakIfStatement* stmt) {
     TINT_ASSERT(Writer, !backedge_stack_.empty());
-    const auto cond_id = GenerateExpressionWithLoadIfNeeded(stmt->condition);
+    const auto cond_id = GenerateExpression(stmt->condition);
     if (!cond_id) {
         return false;
     }
@@ -555,14 +556,19 @@
     return true;
 }
 
-uint32_t Builder::GenerateExpression(const ast::Expression* expr) {
-    if (auto* sem = builder_.Sem().Get(expr)) {
-        if (auto constant = sem->ConstantValue()) {
-            return GenerateConstantIfNeeded(constant);
+uint32_t Builder::GenerateExpression(const sem::Expression* expr) {
+    if (auto* constant = expr->ConstantValue()) {
+        return GenerateConstantIfNeeded(constant);
+    }
+    if (auto* load = expr->As<sem::Load>()) {
+        auto ref_id = GenerateExpression(load->Reference());
+        if (ref_id == 0) {
+            return 0;
         }
+        return GenerateLoad(load->ReferenceType(), ref_id);
     }
     return Switch(
-        expr,  //
+        expr->Declaration(),  //
         [&](const ast::IndexAccessorExpression* a) { return GenerateAccessorExpression(a); },
         [&](const ast::BinaryExpression* b) { return GenerateBinaryExpression(b); },
         [&](const ast::BitcastExpression* b) { return GenerateBitcastExpression(b); },
@@ -577,6 +583,10 @@
         });
 }
 
+uint32_t Builder::GenerateExpression(const ast::Expression* expr) {
+    return GenerateExpression(builder_.Sem().Get(expr));
+}
+
 bool Builder::GenerateFunction(const ast::Function* func_ast) {
     auto* func = builder_.Sem().Get(func_ast);
 
@@ -686,7 +696,7 @@
 
     uint32_t init_id = 0;
     if (v->initializer) {
-        init_id = GenerateExpressionWithLoadIfNeeded(v->initializer);
+        init_id = GenerateExpression(v->initializer);
         if (init_id == 0) {
             return false;
         }
@@ -874,7 +884,7 @@
 }
 
 bool Builder::GenerateIndexAccessor(const ast::IndexAccessorExpression* expr, AccessorInfo* info) {
-    auto idx_id = GenerateExpressionWithLoadIfNeeded(expr->index);
+    auto idx_id = GenerateExpression(expr->index);
     if (idx_id == 0) {
         return 0;
     }
@@ -884,7 +894,7 @@
     // See https://github.com/gpuweb/gpuweb/pull/1580
     if (info->source_type->Is<type::Reference>()) {
         info->access_chain_indices.push_back(idx_id);
-        info->source_type = TypeOf(expr);
+        info->source_type = builder_.Sem().Get(expr)->UnwrapLoad()->Type();
         return true;
     }
 
@@ -936,7 +946,7 @@
 
 bool Builder::GenerateMemberAccessor(const ast::MemberAccessorExpression* expr,
                                      AccessorInfo* info) {
-    auto* expr_sem = builder_.Sem().Get(expr);
+    auto* expr_sem = builder_.Sem().Get(expr)->UnwrapLoad();
     auto* expr_type = expr_sem->Type();
 
     if (auto* access = expr_sem->As<sem::StructMemberAccess>()) {
@@ -1108,7 +1118,7 @@
     }
 
     if (!info.access_chain_indices.empty()) {
-        auto* type = TypeOf(expr);
+        auto* type = builder_.Sem().Get(expr)->UnwrapLoad()->Type();
         auto result_type_id = GenerateTypeIfNeeded(type);
         if (result_type_id == 0) {
             return 0;
@@ -1133,38 +1143,18 @@
 
 uint32_t Builder::GenerateIdentifierExpression(const ast::IdentifierExpression* expr) {
     auto* sem = builder_.Sem().Get(expr);
-    if (auto* user = sem->As<sem::VariableUser>()) {
-        return LookupVariableID(user->Variable());
+    if (sem) {
+        if (auto* user = sem->UnwrapLoad()->As<sem::VariableUser>()) {
+            return LookupVariableID(user->Variable());
+        }
     }
     error_ = "identifier '" + builder_.Symbols().NameFor(expr->symbol) +
              "' does not resolve to a variable";
     return 0;
 }
 
-uint32_t Builder::GenerateExpressionWithLoadIfNeeded(const sem::Expression* expr) {
-    // The semantic node directly knows both the AST node and the resolved type.
-    if (const auto id = GenerateExpression(expr->Declaration())) {
-        return GenerateLoadIfNeeded(expr->Type(), id);
-    }
-    return 0;
-}
-
-uint32_t Builder::GenerateExpressionWithLoadIfNeeded(const ast::Expression* expr) {
-    if (const auto id = GenerateExpression(expr)) {
-        // Perform a lookup to get the resolved type.
-        return GenerateLoadIfNeeded(TypeOf(expr), id);
-    }
-    return 0;
-}
-
-uint32_t Builder::GenerateLoadIfNeeded(const type::Type* type, uint32_t id) {
-    if (auto* ref = type->As<type::Reference>()) {
-        type = ref->StoreType();
-    } else {
-        return id;
-    }
-
-    auto type_id = GenerateTypeIfNeeded(type);
+uint32_t Builder::GenerateLoad(const type::Reference* type, uint32_t id) {
+    auto type_id = GenerateTypeIfNeeded(type->StoreType());
     auto result = result_op();
     auto result_id = std::get<uint32_t>(result);
     if (!push_function_inst(spv::Op::OpLoad, {Operand(type_id), result, Operand(id)})) {
@@ -1173,6 +1163,13 @@
     return result_id;
 }
 
+uint32_t Builder::GenerateLoadIfNeeded(const type::Type* type, uint32_t id) {
+    if (auto* ref = type->As<type::Reference>()) {
+        return GenerateLoad(ref, id);
+    }
+    return id;
+}
+
 uint32_t Builder::GenerateUnaryOpExpression(const ast::UnaryOpExpression* expr) {
     auto result = result_op();
     auto result_id = std::get<uint32_t>(result);
@@ -1200,7 +1197,7 @@
             return GenerateExpression(expr->expr);
     }
 
-    auto val_id = GenerateExpressionWithLoadIfNeeded(expr->expr);
+    auto val_id = GenerateExpression(expr->expr);
     if (val_id == 0) {
         return 0;
     }
@@ -1338,7 +1335,7 @@
 
     for (auto* e : args) {
         uint32_t id = 0;
-        id = GenerateExpressionWithLoadIfNeeded(e);
+        id = GenerateExpression(e);
         if (id == 0) {
             return 0;
         }
@@ -1476,7 +1473,7 @@
         return 0;
     }
 
-    auto val_id = GenerateExpressionWithLoadIfNeeded(from_expr);
+    auto val_id = GenerateExpression(from_expr);
     if (val_id == 0) {
         return 0;
     }
@@ -1828,7 +1825,7 @@
 }
 
 uint32_t Builder::GenerateShortCircuitBinaryExpression(const ast::BinaryExpression* expr) {
-    auto lhs_id = GenerateExpressionWithLoadIfNeeded(expr->lhs);
+    auto lhs_id = GenerateExpression(expr->lhs);
     if (lhs_id == 0) {
         return false;
     }
@@ -1869,7 +1866,7 @@
     if (!GenerateLabel(block_id)) {
         return 0;
     }
-    auto rhs_id = GenerateExpressionWithLoadIfNeeded(expr->rhs);
+    auto rhs_id = GenerateExpression(expr->rhs);
     if (rhs_id == 0) {
         return 0;
     }
@@ -1989,12 +1986,12 @@
         return GenerateShortCircuitBinaryExpression(expr);
     }
 
-    auto lhs_id = GenerateExpressionWithLoadIfNeeded(expr->lhs);
+    auto lhs_id = GenerateExpression(expr->lhs);
     if (lhs_id == 0) {
         return 0;
     }
 
-    auto rhs_id = GenerateExpressionWithLoadIfNeeded(expr->rhs);
+    auto rhs_id = GenerateExpression(expr->rhs);
     if (rhs_id == 0) {
         return 0;
     }
@@ -2267,7 +2264,7 @@
     ops.push_back(Operand(func_id));
 
     for (auto* arg : expr->args) {
-        auto id = GenerateExpressionWithLoadIfNeeded(arg);
+        auto id = GenerateExpression(arg);
         if (id == 0) {
             return 0;
         }
@@ -2623,10 +2620,7 @@
     auto& arguments = call->Arguments();
 
     // Generates the given expression, returning the operand ID
-    auto gen = [&](const sem::Expression* expr) {
-        const auto val_id = GenerateExpressionWithLoadIfNeeded(expr);
-        return Operand(val_id);
-    };
+    auto gen = [&](const sem::Expression* expr) { return Operand(GenerateExpression(expr)); };
 
     // Returns the argument with the given usage
     auto arg = [&](Usage usage) {
@@ -2754,7 +2748,7 @@
             // Array index needs to be appended to the coordinates.
             auto* packed = AppendVector(&builder_, arg(Usage::kCoords)->Declaration(),
                                         array_index->Declaration());
-            auto param = GenerateExpression(packed->Declaration());
+            auto param = GenerateExpression(packed);
             if (param == 0) {
                 return false;
             }
@@ -3097,14 +3091,14 @@
         return false;
     }
 
-    uint32_t pointer_id = GenerateExpression(call->Arguments()[0]->Declaration());
+    uint32_t pointer_id = GenerateExpression(call->Arguments()[0]);
     if (pointer_id == 0) {
         return false;
     }
 
     uint32_t value_id = 0;
     if (call->Arguments().Length() > 1) {
-        value_id = GenerateExpressionWithLoadIfNeeded(call->Arguments().Back());
+        value_id = GenerateExpression(call->Arguments().Back());
         if (value_id == 0) {
             return false;
         }
@@ -3206,8 +3200,7 @@
                                                                      value,
                                                                  });
         case sem::BuiltinType::kAtomicCompareExchangeWeak: {
-            auto comparator =
-                GenerateExpressionWithLoadIfNeeded(call->Arguments()[1]->Declaration());
+            auto comparator = GenerateExpression(call->Arguments()[1]);
             if (comparator == 0) {
                 return false;
             }
@@ -3312,7 +3305,7 @@
         return 0;
     }
 
-    auto val_id = GenerateExpressionWithLoadIfNeeded(expr->expr);
+    auto val_id = GenerateExpression(expr->expr);
     if (val_id == 0) {
         return 0;
     }
@@ -3339,7 +3332,7 @@
 bool Builder::GenerateConditionalBlock(const ast::Expression* cond,
                                        const ast::BlockStatement* true_body,
                                        const ast::Statement* else_stmt) {
-    auto cond_id = GenerateExpressionWithLoadIfNeeded(cond);
+    auto cond_id = GenerateExpression(cond);
     if (cond_id == 0) {
         return false;
     }
@@ -3420,7 +3413,7 @@
 
     merge_stack_.push_back(merge_block_id);
 
-    auto cond_id = GenerateExpressionWithLoadIfNeeded(stmt->condition);
+    auto cond_id = GenerateExpression(stmt->condition);
     if (cond_id == 0) {
         return false;
     }
@@ -3504,7 +3497,7 @@
 
 bool Builder::GenerateReturnStatement(const ast::ReturnStatement* stmt) {
     if (stmt->value) {
-        auto val_id = GenerateExpressionWithLoadIfNeeded(stmt->value);
+        auto val_id = GenerateExpression(stmt->value);
         if (val_id == 0) {
             return false;
         }
diff --git a/src/tint/writer/spirv/builder.h b/src/tint/writer/spirv/builder.h
index 68c8f25..6ac749e 100644
--- a/src/tint/writer/spirv/builder.h
+++ b/src/tint/writer/spirv/builder.h
@@ -43,6 +43,7 @@
 // Forward declarations
 namespace tint::sem {
 class Call;
+class Load;
 class TypeInitializer;
 class TypeConversion;
 }  // namespace tint::sem
@@ -274,6 +275,10 @@
     /// Generates an expression
     /// @param expr the expression to generate
     /// @returns the resulting ID of the expression or 0 on error
+    uint32_t GenerateExpression(const sem::Expression* expr);
+    /// Generates an expression
+    /// @param expr the expression to generate
+    /// @returns the resulting ID of the expression or 0 on error
     uint32_t GenerateExpression(const ast::Expression* expr);
     /// Generates the instructions for a function
     /// @param func the function to generate
@@ -440,24 +445,15 @@
     /// @param stmt the statement to generate
     /// @returns true if the statement was generated
     bool GenerateStatement(const ast::Statement* stmt);
-    /// Generates an expression. If the WGSL expression does not have reference
-    /// type, then return the SPIR-V ID for the expression. Otherwise implement
-    /// the WGSL Load Rule: generate an OpLoad and return the ID of the result.
-    /// Returns 0 if the expression could not be generated.
-    /// @param expr the semantic expression node to be generated
-    /// @returns the the ID of the expression, or loaded expression
-    uint32_t GenerateExpressionWithLoadIfNeeded(const sem::Expression* expr);
-    /// Generates an expression. If the WGSL expression does not have reference
-    /// type, then return the SPIR-V ID for the expression. Otherwise implement
-    /// the WGSL Load Rule: generate an OpLoad and return the ID of the result.
-    /// Returns 0 if the expression could not be generated.
-    /// @param expr the AST expression to be generated
-    /// @returns the the ID of the expression, or loaded expression
-    uint32_t GenerateExpressionWithLoadIfNeeded(const ast::Expression* expr);
-    /// Generates an OpLoad on the given ID if it has reference type in WGSL,
-    /// othewrise return the ID itself.
+    /// Generates an OpLoad of the given expression type
+    /// @param type the reference type of the expression
+    /// @param id the SPIR-V id of the expression
+    /// @returns the ID of the loaded value or 0 on failure.
+    uint32_t GenerateLoad(const type::Reference* type, uint32_t id);
+    /// Generates an OpLoad on the given ID if it has reference type in WGSL, otherwise return the
+    /// ID itself.
     /// @param type the type of the expression
-    /// @param id the SPIR-V id of the experssion
+    /// @param id the SPIR-V id of the expression
     /// @returns the ID of the loaded value or `id` if type is not a reference
     uint32_t GenerateLoadIfNeeded(const type::Type* type, uint32_t id);
     /// Generates an OpStore. Emits an error and returns false if we're
diff --git a/src/tint/writer/spirv/builder_accessor_expression_test.cc b/src/tint/writer/spirv/builder_accessor_expression_test.cc
index 5c06717..925406a 100644
--- a/src/tint/writer/spirv/builder_accessor_expression_test.cc
+++ b/src/tint/writer/spirv/builder_accessor_expression_test.cc
@@ -1263,12 +1263,12 @@
 %7 = OpTypeVector %8 3
 %6 = OpTypePointer Function %7
 %9 = OpConstantNull %7
-%10 = OpTypeVector %8 2
+%11 = OpTypeVector %8 2
 )");
     EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%11 = OpLoad %7 %5
-%12 = OpVectorShuffle %10 %11 %11 1 0
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%10 = OpLoad %7 %5
+%12 = OpVectorShuffle %11 %10 %10 1 0
 OpReturn
 )");