[tint] Reduce all hash codes to 32 bits

Change-Id: Iab9c324b928265d094336e0ca7f68fd4ed135e6e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/172541
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/api/common/binding_point.h b/src/tint/api/common/binding_point.h
index b602c81..2c6baa5 100644
--- a/src/tint/api/common/binding_point.h
+++ b/src/tint/api/common/binding_point.h
@@ -48,6 +48,9 @@
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
     TINT_REFLECT(BindingPoint, group, binding);
 
+    /// @returns the hash code of the BindingPoint
+    tint::HashCode HashCode() const { return tint::Hash(group, binding); }
+
     /// Equality operator
     /// @param rhs the BindingPoint to compare against
     /// @returns true if this BindingPoint is equal to `rhs`
@@ -97,8 +100,9 @@
   public:
     /// @param binding_point the binding point to create a hash for
     /// @return the hash value
-    inline std::size_t operator()(const tint::BindingPoint& binding_point) const {
-        return tint::Hash(binding_point.group, binding_point.binding);
+    inline size_t operator()(const tint::BindingPoint& binding_point) const {
+        return static_cast<size_t>(binding_point.group) << 16 |
+               static_cast<size_t>(binding_point.binding);
     }
 };
 
diff --git a/src/tint/api/common/override_id.h b/src/tint/api/common/override_id.h
index f7a6837..99e9d6a 100644
--- a/src/tint/api/common/override_id.h
+++ b/src/tint/api/common/override_id.h
@@ -31,6 +31,7 @@
 #include <stdint.h>
 #include <functional>
 
+#include "src/tint/utils/math/hash.h"
 #include "src/tint/utils/reflection/reflection.h"
 
 namespace tint {
@@ -42,6 +43,9 @@
 
     /// Reflect the fields of this struct so that it can be used by tint::ForeachField()
     TINT_REFLECT(OverrideId, value);
+
+    /// @returns the hash code of the OverrideId
+    tint::HashCode HashCode() const { return Hash(value); }
 };
 
 /// Ensure that all the fields of OverrideId are reflected.
@@ -73,7 +77,7 @@
   public:
     /// @param id the override identifier
     /// @return the hash of the override identifier
-    inline std::size_t operator()(tint::OverrideId id) const {
+    inline size_t operator()(tint::OverrideId id) const {
         return std::hash<decltype(tint::OverrideId::value)>()(id.value);
     }
 };
diff --git a/src/tint/fuzzers/random_generator.cc b/src/tint/fuzzers/random_generator.cc
index 3965b1c..5bcf4cd 100644
--- a/src/tint/fuzzers/random_generator.cc
+++ b/src/tint/fuzzers/random_generator.cc
@@ -33,7 +33,6 @@
 
 #include "src/tint/fuzzers/mersenne_twister_engine.h"
 #include "src/tint/fuzzers/random_generator_engine.h"
-#include "src/tint/utils/math/hash.h"
 
 namespace tint::fuzzers {
 
@@ -47,9 +46,9 @@
 /// @param size - number of elements in buffer
 /// @returns hash of the data in the buffer
 size_t HashBuffer(const uint8_t* data, const size_t size) {
-    size_t hash = Hash(size);
+    size_t hash = std::hash<size_t>{}(size);
     for (size_t i = 0; i < size; i++) {
-        hash = HashCombine(hash, data[i]);
+        hash ^= (static_cast<uint64_t>(data[i]) * 31) + (0x7f4a7c16 ^ (hash >> 2));
     }
     return hash;
 }
diff --git a/src/tint/lang/core/constant/composite.h b/src/tint/lang/core/constant/composite.h
index 5844d96..1b359fe 100644
--- a/src/tint/lang/core/constant/composite.h
+++ b/src/tint/lang/core/constant/composite.h
@@ -69,7 +69,7 @@
     bool AnyZero() const override { return any_zero; }
 
     /// @copydoc Value::Hash()
-    size_t Hash() const override { return hash; }
+    HashCode Hash() const override { return hash; }
 
     /// Clones the constant into the provided context
     /// @param ctx the clone context
@@ -85,14 +85,14 @@
     /// True if any element is zero
     const bool any_zero;
     /// The hash of the composite
-    const size_t hash;
+    const HashCode hash;
 
   protected:
     /// @copydoc Value::InternalValue()
     std::variant<std::monostate, AInt, AFloat> InternalValue() const override { return {}; }
 
   private:
-    size_t CalcHash() {
+    HashCode CalcHash() {
         auto h = tint::Hash(type, all_zero, any_zero);
         for (auto* el : elements) {
             h = HashCombine(h, el->Hash());
diff --git a/src/tint/lang/core/constant/manager.h b/src/tint/lang/core/constant/manager.h
index 9fdfac8..993a388 100644
--- a/src/tint/lang/core/constant/manager.h
+++ b/src/tint/lang/core/constant/manager.h
@@ -155,7 +155,7 @@
     struct Hasher {
         /// @param value the value to hash
         /// @returns a hash of the value
-        size_t operator()(const constant::Value& value) const { return value.Hash(); }
+        HashCode operator()(const constant::Value& value) const { return value.Hash(); }
     };
 
     /// An equality helper for constant::Value
diff --git a/src/tint/lang/core/constant/scalar.h b/src/tint/lang/core/constant/scalar.h
index bd9724e..29a3c69 100644
--- a/src/tint/lang/core/constant/scalar.h
+++ b/src/tint/lang/core/constant/scalar.h
@@ -77,7 +77,7 @@
     bool AnyZero() const override { return IsZero(); }
 
     /// @copydoc Value::Hash()
-    size_t Hash() const override { return tint::Hash(type, ValueOf()); }
+    HashCode Hash() const override { return tint::Hash(type, ValueOf()); }
 
     /// Clones the constant into the provided context
     /// @param ctx the clone context
diff --git a/src/tint/lang/core/constant/splat.h b/src/tint/lang/core/constant/splat.h
index a65ea83..bf7a0c4 100644
--- a/src/tint/lang/core/constant/splat.h
+++ b/src/tint/lang/core/constant/splat.h
@@ -65,7 +65,7 @@
     bool AnyZero() const override { return el->AnyZero(); }
 
     /// @returns the hash for the splat
-    size_t Hash() const override { return tint::Hash(type, el->Hash(), count); }
+    HashCode Hash() const override { return tint::Hash(type, el->Hash(), count); }
 
     /// Clones the constant into the provided context
     /// @param ctx the clone context
diff --git a/src/tint/lang/core/constant/value.h b/src/tint/lang/core/constant/value.h
index 98c7bcb..c0ce056 100644
--- a/src/tint/lang/core/constant/value.h
+++ b/src/tint/lang/core/constant/value.h
@@ -70,7 +70,7 @@
     virtual bool AnyZero() const = 0;
 
     /// @returns a hash of the value.
-    virtual size_t Hash() const = 0;
+    virtual HashCode Hash() const = 0;
 
     /// @returns the value as the given scalar or abstract value.
     template <typename T>
diff --git a/src/tint/lang/core/intrinsic/table.h b/src/tint/lang/core/intrinsic/table.h
index 32f60fc..c1cbfcc 100644
--- a/src/tint/lang/core/intrinsic/table.h
+++ b/src/tint/lang/core/intrinsic/table.h
@@ -295,8 +295,8 @@
 struct Hasher<core::intrinsic::Overload> {
     /// @param i the core::intrinsic::Overload to create a hash for
     /// @return the hash value
-    inline std::size_t operator()(const core::intrinsic::Overload& i) const {
-        size_t hash = Hash(i.parameters.Length());
+    inline HashCode operator()(const core::intrinsic::Overload& i) const {
+        HashCode hash = Hash(i.parameters.Length());
         for (auto& p : i.parameters) {
             hash = HashCombine(hash, p.type, p.usage);
         }
diff --git a/src/tint/lang/core/ir/disassembler.h b/src/tint/lang/core/ir/disassembler.h
index c585fc4..d22b8a1 100644
--- a/src/tint/lang/core/ir/disassembler.h
+++ b/src/tint/lang/core/ir/disassembler.h
@@ -64,7 +64,7 @@
         size_t index = 0u;
 
         /// @returns the hash code of the IndexedValue
-        size_t HashCode() const { return Hash(instruction, index); }
+        tint::HashCode HashCode() const { return Hash(instruction, index); }
 
         /// An equality helper for IndexedValue.
         /// @param other the IndexedValue to compare against
diff --git a/src/tint/lang/core/ir/transform/direct_variable_access.cc b/src/tint/lang/core/ir/transform/direct_variable_access.cc
index 5297c81..ca89b39 100644
--- a/src/tint/lang/core/ir/transform/direct_variable_access.cc
+++ b/src/tint/lang/core/ir/transform/direct_variable_access.cc
@@ -53,7 +53,7 @@
     Var* var = nullptr;
 
     /// @return a hash value for this object
-    size_t HashCode() const { return Hash(var); }
+    tint::HashCode HashCode() const { return Hash(var); }
 
     /// Inequality operator
     bool operator!=(const RootModuleScopeVar& other) const { return var != other.var; }
@@ -66,7 +66,7 @@
     const type::Pointer* type = nullptr;
 
     /// @return a hash value for this object
-    size_t HashCode() const { return Hash(type); }
+    tint::HashCode HashCode() const { return Hash(type); }
 
     /// Inequality operator
     bool operator!=(const RootPtrParameter& other) const { return type != other.type; }
@@ -81,7 +81,7 @@
     const type::StructMember* member;
 
     /// @return a hash member for this object
-    size_t HashCode() const { return Hash(member); }
+    tint::HashCode HashCode() const { return Hash(member); }
 
     /// Inequality operator
     bool operator!=(const MemberAccess& other) const { return member != other.member; }
@@ -91,7 +91,7 @@
 /// The ordered list of indices is passed by parameter.
 struct IndexAccess {
     /// @return a hash value for this object
-    size_t HashCode() const { return 42; }
+    tint::HashCode HashCode() const { return 42; }
 
     /// Inequality operator
     bool operator!=(const IndexAccess&) const { return false; }
@@ -166,7 +166,7 @@
     }
 
     /// @return a hash value for this object
-    size_t HashCode() const { return Hash(root, ops); }
+    tint::HashCode HashCode() const { return Hash(root, ops); }
 
     /// Inequality operator
     bool operator!=(const AccessShape& other) const {
diff --git a/src/tint/lang/core/ir/value.h b/src/tint/lang/core/ir/value.h
index e2819bc..0603e87 100644
--- a/src/tint/lang/core/ir/value.h
+++ b/src/tint/lang/core/ir/value.h
@@ -48,7 +48,7 @@
     size_t operand_index = 0u;
 
     /// @returns the hash code of the Usage
-    size_t HashCode() const { return Hash(instruction, operand_index); }
+    tint::HashCode HashCode() const { return Hash(instruction, operand_index); }
 
     /// An equality helper for Usage.
     /// @param other the usage to compare against
diff --git a/src/tint/lang/spirv/reader/ast_parser/type.cc b/src/tint/lang/spirv/reader/ast_parser/type.cc
index 2c29979..5c62f29 100644
--- a/src/tint/lang/spirv/reader/ast_parser/type.cc
+++ b/src/tint/lang/spirv/reader/ast_parser/type.cc
@@ -70,55 +70,57 @@
 namespace {
 
 struct PointerHasher {
-    size_t operator()(const Pointer& t) const { return Hash(t.address_space, t.type, t.access); }
+    HashCode operator()(const Pointer& t) const { return Hash(t.address_space, t.type, t.access); }
 };
 
 struct ReferenceHasher {
-    size_t operator()(const Reference& t) const { return Hash(t.address_space, t.type, t.access); }
+    HashCode operator()(const Reference& t) const {
+        return Hash(t.address_space, t.type, t.access);
+    }
 };
 
 struct VectorHasher {
-    size_t operator()(const Vector& t) const { return Hash(t.type, t.size); }
+    HashCode operator()(const Vector& t) const { return Hash(t.type, t.size); }
 };
 
 struct MatrixHasher {
-    size_t operator()(const Matrix& t) const { return Hash(t.type, t.columns, t.rows); }
+    HashCode operator()(const Matrix& t) const { return Hash(t.type, t.columns, t.rows); }
 };
 
 struct ArrayHasher {
-    size_t operator()(const Array& t) const { return Hash(t.type, t.size, t.stride); }
+    HashCode operator()(const Array& t) const { return Hash(t.type, t.size, t.stride); }
 };
 
 struct AliasHasher {
-    size_t operator()(const Alias& t) const { return Hash(t.name); }
+    HashCode operator()(const Alias& t) const { return Hash(t.name); }
 };
 
 struct StructHasher {
-    size_t operator()(const Struct& t) const { return Hash(t.name); }
+    HashCode operator()(const Struct& t) const { return Hash(t.name); }
 };
 
 struct SamplerHasher {
-    size_t operator()(const Sampler& s) const { return Hash(s.kind); }
+    HashCode operator()(const Sampler& s) const { return Hash(s.kind); }
 };
 
 struct DepthTextureHasher {
-    size_t operator()(const DepthTexture& t) const { return Hash(t.dims); }
+    HashCode operator()(const DepthTexture& t) const { return Hash(t.dims); }
 };
 
 struct DepthMultisampledTextureHasher {
-    size_t operator()(const DepthMultisampledTexture& t) const { return Hash(t.dims); }
+    HashCode operator()(const DepthMultisampledTexture& t) const { return Hash(t.dims); }
 };
 
 struct MultisampledTextureHasher {
-    size_t operator()(const MultisampledTexture& t) const { return Hash(t.dims, t.type); }
+    HashCode operator()(const MultisampledTexture& t) const { return Hash(t.dims, t.type); }
 };
 
 struct SampledTextureHasher {
-    size_t operator()(const SampledTexture& t) const { return Hash(t.dims, t.type); }
+    HashCode operator()(const SampledTexture& t) const { return Hash(t.dims, t.type); }
 };
 
 struct StorageTextureHasher {
-    size_t operator()(const StorageTexture& t) const { return Hash(t.dims, t.format, t.access); }
+    HashCode operator()(const StorageTexture& t) const { return Hash(t.dims, t.format, t.access); }
 };
 }  // namespace
 
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index 1976c38..6023194 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -669,14 +669,8 @@
             return type == other.type && access_mode == other.access_mode;
         }
 
-        /// Hasher provides a hash function for the TypeKey.
-        struct Hasher {
-            /// @param tk the TypeKey to create a hash for
-            /// @return the hash value
-            inline std::size_t operator()(const TypeKey& tk) const {
-                return HashCombine(Hash(tk.type), tk.access_mode);
-            }
-        };
+        /// @returns the hash code of the TypeKey
+        tint::HashCode HashCode() const { return Hash(type, access_mode); }
     };
 
     /// The generated IR module.
@@ -691,7 +685,7 @@
     /// The Tint IR block that is currently being emitted.
     core::ir::Block* current_block_ = nullptr;
     /// A map from a SPIR-V type declaration to the corresponding Tint type object.
-    Hashmap<TypeKey, const core::type::Type*, 16, TypeKey::Hasher> types_;
+    Hashmap<TypeKey, const core::type::Type*, 16> types_;
     /// A map from a SPIR-V function definition result ID to the corresponding Tint function object.
     Hashmap<uint32_t, core::ir::Function*, 8> functions_;
     /// A map from a SPIR-V result ID to the corresponding Tint value object.
diff --git a/src/tint/lang/spirv/writer/ast_printer/builder.cc b/src/tint/lang/spirv/writer/ast_printer/builder.cc
index bcd175e..eb393ca 100644
--- a/src/tint/lang/spirv/writer/ast_printer/builder.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/builder.cc
@@ -622,7 +622,7 @@
 }
 
 uint32_t Builder::GenerateFunctionTypeIfNeeded(const sem::Function* func) {
-    return tint::GetOrAdd(func_sig_to_id_, func->Signature(), [&]() -> uint32_t {
+    return func_sig_to_id_.GetOrAdd(func->Signature(), [&]() -> uint32_t {
         auto func_op = result_op();
         auto func_type_id = std::get<uint32_t>(func_op);
 
diff --git a/src/tint/lang/spirv/writer/ast_printer/builder.h b/src/tint/lang/spirv/writer/ast_printer/builder.h
index bd74c08..1e39a34 100644
--- a/src/tint/lang/spirv/writer/ast_printer/builder.h
+++ b/src/tint/lang/spirv/writer/ast_printer/builder.h
@@ -549,7 +549,7 @@
     std::unordered_map<uint32_t, const sem::Variable*> id_to_var_;
     std::unordered_map<std::string, uint32_t> import_name_to_id_;
     std::unordered_map<Symbol, uint32_t> func_symbol_to_id_;
-    std::unordered_map<sem::CallTargetSignature, uint32_t> func_sig_to_id_;
+    Hashmap<sem::CallTargetSignature, uint32_t, 4> func_sig_to_id_;
     std::unordered_map<const core::type::Type*, uint32_t> type_to_id_;
     std::unordered_map<ScalarConstant, uint32_t> const_to_id_;
     std::unordered_map<const core::type::Type*, uint32_t> const_null_to_id_;
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index 9f243f4..ef41faf 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -225,18 +225,14 @@
         uint32_t return_type_id;
         Vector<uint32_t, 4> param_type_ids;
 
-        /// Hasher provides a hash function for the FunctionType.
-        struct Hasher {
-            /// @param ft the FunctionType to create a hash for
-            /// @return the hash value
-            inline std::size_t operator()(const FunctionType& ft) const {
-                size_t hash = Hash(ft.return_type_id);
-                for (auto& p : ft.param_type_ids) {
-                    hash = HashCombine(hash, p);
-                }
-                return hash;
+        /// @returns the hash code of the FunctionType
+        tint::HashCode HashCode() const {
+            auto hash = Hash(return_type_id);
+            for (auto& p : param_type_ids) {
+                hash = HashCombine(hash, p);
             }
-        };
+            return hash;
+        }
 
         /// Equality operator for FunctionType.
         bool operator==(const FunctionType& other) const {
@@ -249,7 +245,7 @@
     Hashmap<const core::type::Type*, uint32_t, 8> types_;
 
     /// The map of function types to their result IDs.
-    Hashmap<FunctionType, uint32_t, 8, FunctionType::Hasher> function_types_;
+    Hashmap<FunctionType, uint32_t, 8> function_types_;
 
     /// The map of constants to their result IDs.
     Hashmap<const core::constant::Value*, uint32_t, 16> constants_;
diff --git a/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc b/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc
index 19037f7..cd41b6e 100644
--- a/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc
+++ b/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc
@@ -65,12 +65,8 @@
     // The list of constant indices to get from the base to the source object.
     Vector<core::ir::Value*, 4> indices;
 
-    // A specialization of Hasher for PartialAccess.
-    struct Hasher {
-        inline std::size_t operator()(const PartialAccess& src) const {
-            return Hash(src.base, src.indices);
-        }
-    };
+    /// @returns the hash code of the PartialAccess
+    tint::HashCode HashCode() const { return Hash(base, indices); }
 
     // An equality helper for PartialAccess.
     bool operator==(const PartialAccess& other) const {
@@ -139,7 +135,7 @@
 
     // Replace each access instruction that we recorded.
     Hashmap<core::ir::Value*, core::ir::Value*, 4> object_to_local;
-    Hashmap<PartialAccess, core::ir::Value*, 4, PartialAccess::Hasher> source_object_to_value;
+    Hashmap<PartialAccess, core::ir::Value*, 4> source_object_to_value;
     for (const auto& to_replace : worklist) {
         auto* access = to_replace.access;
         auto* source_object = access->Object();
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc b/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
index 7cdee65..d92bfb0 100644
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
+++ b/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
@@ -73,7 +73,7 @@
     tint::core::AddressSpace address_space = tint::core::AddressSpace::kUndefined;
 
     /// @return a hash code for this object
-    size_t HashCode() const { return Hash(type, variable); }
+    tint::HashCode HashCode() const { return Hash(type, variable); }
 };
 
 /// Inequality operator for AccessRoot
@@ -85,7 +85,7 @@
 /// vector index.
 struct DynamicIndex {
     /// @return a hash code for this object
-    size_t HashCode() const { return 42 /* empty struct: any number will do */; }
+    tint::HashCode HashCode() const { return 42 /* empty struct: any number will do */; }
 };
 
 /// Inequality operator for DynamicIndex
@@ -160,7 +160,7 @@
     }
 
     /// @return a hash code for this object
-    size_t HashCode() const { return Hash(root, ops); }
+    tint::HashCode HashCode() const { return Hash(root, ops); }
 };
 
 /// Equality operator for AccessShape
diff --git a/src/tint/lang/wgsl/ast/transform/std140.cc b/src/tint/lang/wgsl/ast/transform/std140.cc
index 037d716..9dc49b5 100644
--- a/src/tint/lang/wgsl/ast/transform/std140.cc
+++ b/src/tint/lang/wgsl/ast/transform/std140.cc
@@ -57,7 +57,7 @@
 /// UniformVariable is used by Std140::State::AccessIndex to indicate the root uniform variable
 struct UniformVariable {
     /// @returns a hash code for this object
-    size_t HashCode() const { return 0; }
+    tint::HashCode HashCode() const { return 0; }
 };
 
 /// Inequality operator for UniformVariable
@@ -70,7 +70,7 @@
     size_t slot;  // The index of the expression in Std140::State::AccessChain::dynamic_indices
 
     /// @returns a hash code for this object
-    size_t HashCode() const { return Hash(slot); }
+    tint::HashCode HashCode() const { return Hash(slot); }
 };
 
 /// Inequality operator for DynamicIndex
@@ -193,12 +193,8 @@
         /// The chain of accesses indices.
         AccessIndices indices;
 
-        /// Hash function for LoadFnKey.
-        struct Hasher {
-            /// @param fn the LoadFnKey to hash
-            /// @return the hash for the given LoadFnKey
-            size_t operator()(const LoadFnKey& fn) const { return Hash(fn.var, fn.indices); }
-        };
+        /// @returns the hash code for the LoadFnKey
+        tint::HashCode HashCode() const { return Hash(var, indices); }
 
         /// Equality operator
         bool operator==(const LoadFnKey& other) const {
@@ -218,7 +214,7 @@
     const SymbolTable& sym = src.Symbols();
 
     /// Map of load function signature, to the generated function
-    Hashmap<LoadFnKey, Symbol, 8, LoadFnKey::Hasher> load_fns;
+    Hashmap<LoadFnKey, Symbol, 8> load_fns;
 
     /// Map of std140-forked type to converter function name
     Hashmap<const core::type::Type*, Symbol, 8> conv_fns;
diff --git a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
index d0b303e..e394520 100644
--- a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
+++ b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
@@ -94,23 +94,19 @@
         /// The RHS of the division part of the expression
         uint32_t division = 1;
 
+        /// @returns the hash code of the ArrayIndex
+        tint::HashCode HashCode() const { return Hash(modulo, division); }
+
         /// Equality operator
         /// @param i the ArrayIndex to compare to this ArrayIndex
         /// @returns true if `i` and this ArrayIndex are equal
         bool operator==(const ArrayIndex& i) const {
             return modulo == i.modulo && division == i.division;
         }
-
-        /// Hash function for the ArrayIndex type
-        struct Hasher {
-            /// @param i the ArrayIndex to calculate a hash for
-            /// @returns the hash value for the ArrayIndex `i`
-            size_t operator()(const ArrayIndex& i) const { return Hash(i.modulo, i.division); }
-        };
     };
 
     /// A list of unique ArrayIndex
-    using ArrayIndices = UniqueVector<ArrayIndex, 4, ArrayIndex::Hasher>;
+    using ArrayIndices = UniqueVector<ArrayIndex, 4>;
 
     /// Expression holds information about an expression that is being built for a
     /// statement will zero workgroup values.
@@ -142,7 +138,7 @@
 
     /// A map of ArrayIndex to the name reserved for the `let` declaration of that
     /// index.
-    std::unordered_map<ArrayIndex, Symbol, ArrayIndex::Hasher> array_index_names;
+    Hashmap<ArrayIndex, Symbol, 4> array_index_names;
 
     /// Constructor
     /// @param c the program::CloneContext used for the transform
@@ -397,8 +393,8 @@
                 }
                 auto array_indices = a.array_indices;
                 array_indices.Add(ArrayIndex{modulo, division});
-                auto index = tint::GetOrAdd(array_index_names, ArrayIndex{modulo, division},
-                                            [&] { return b.Symbols().New("i"); });
+                auto index = array_index_names.GetOrAdd(ArrayIndex{modulo, division},
+                                                        [&] { return b.Symbols().New("i"); });
                 return Expression{b.IndexAccessor(a.expr, index), a.num_iterations, array_indices};
             };
             return BuildZeroingStatements(arr->ElemType(), get_el);
@@ -422,13 +418,13 @@
         StatementList stmts;
         std::map<Symbol, ArrayIndex> indices_by_name;
         for (auto index : array_indices) {
-            auto name = array_index_names.at(index);
+            auto name = array_index_names.Get(index);
             auto* mod = (num_iterations > index.modulo)
                             ? b.create<BinaryExpression>(core::BinaryOp::kModulo, iteration(),
                                                          b.Expr(u32(index.modulo)))
                             : iteration();
             auto* div = (index.division != 1u) ? b.Div(mod, u32(index.division)) : mod;
-            auto* decl = b.Decl(b.Let(name, b.ty.u32(), div));
+            auto* decl = b.Decl(b.Let(*name, b.ty.u32(), div));
             stmts.Push(decl);
         }
         return stmts;
diff --git a/src/tint/lang/wgsl/resolver/dependency_graph.cc b/src/tint/lang/wgsl/resolver/dependency_graph.cc
index 438acef..cb666d5 100644
--- a/src/tint/lang/wgsl/resolver/dependency_graph.cc
+++ b/src/tint/lang/wgsl/resolver/dependency_graph.cc
@@ -107,22 +107,16 @@
     const Global* from;
     /// The Global that is depended on by #from
     const Global* to;
-};
 
-/// DependencyEdgeCmp implements the contracts of std::equal_to<DependencyEdge>
-/// and std::hash<DependencyEdge>.
-struct DependencyEdgeCmp {
+    /// @returns the hash code of the DependencyEdge
+    tint::HashCode HashCode() const { return Hash(from, to); }
+
     /// Equality operator
-    bool operator()(const DependencyEdge& lhs, const DependencyEdge& rhs) const {
-        return lhs.from == rhs.from && lhs.to == rhs.to;
-    }
-    /// Hashing operator
-    inline std::size_t operator()(const DependencyEdge& d) const { return Hash(d.from, d.to); }
+    bool operator==(const DependencyEdge& rhs) const { return from == rhs.from && to == rhs.to; }
 };
 
 /// A map of DependencyEdge to DependencyInfo
-using DependencyEdges =
-    Hashmap<DependencyEdge, DependencyInfo, 64, DependencyEdgeCmp, DependencyEdgeCmp>;
+using DependencyEdges = Hashmap<DependencyEdge, DependencyInfo, 64>;
 
 /// Global describes a module-scope variable, type or function.
 struct Global {
diff --git a/src/tint/lang/wgsl/resolver/validator.h b/src/tint/lang/wgsl/resolver/validator.h
index 4834073..92ccf7c 100644
--- a/src/tint/lang/wgsl/resolver/validator.h
+++ b/src/tint/lang/wgsl/resolver/validator.h
@@ -98,7 +98,7 @@
     }
 
     /// @returns the hash value of this object
-    std::size_t HashCode() const { return Hash(type, address_space); }
+    tint::HashCode HashCode() const { return Hash(type, address_space); }
 };
 
 /// DiagnosticFilterStack is a scoped stack of diagnostic filters.
diff --git a/src/tint/lang/wgsl/sem/builtin_fn.h b/src/tint/lang/wgsl/sem/builtin_fn.h
index 7c5d12c..bcac245 100644
--- a/src/tint/lang/wgsl/sem/builtin_fn.h
+++ b/src/tint/lang/wgsl/sem/builtin_fn.h
@@ -123,7 +123,7 @@
     wgsl::LanguageFeature RequiredLanguageFeature() const;
 
     /// @return the hash code for this object
-    std::size_t HashCode() const {
+    tint::HashCode HashCode() const {
         return Hash(Fn(), SupportedStages(), ReturnType(), Parameters(), IsDeprecated());
     }
 
diff --git a/src/tint/lang/wgsl/sem/call_target.cc b/src/tint/lang/wgsl/sem/call_target.cc
index 37098f5..1298301 100644
--- a/src/tint/lang/wgsl/sem/call_target.cc
+++ b/src/tint/lang/wgsl/sem/call_target.cc
@@ -71,6 +71,14 @@
     return -1;
 }
 
+tint::HashCode CallTargetSignature::HashCode() const {
+    auto hash = tint::Hash(parameters.Length());
+    for (auto* p : parameters) {
+        hash = HashCombine(hash, p->Type(), p->Usage());
+    }
+    return Hash(hash, return_type);
+}
+
 bool CallTargetSignature::operator==(const CallTargetSignature& other) const {
     if (return_type != other.return_type || parameters.Length() != other.parameters.Length()) {
         return false;
@@ -86,16 +94,3 @@
 }
 
 }  // namespace tint::sem
-
-namespace std {
-
-std::size_t hash<tint::sem::CallTargetSignature>::operator()(
-    const tint::sem::CallTargetSignature& sig) const {
-    size_t hash = tint::Hash(sig.parameters.Length());
-    for (auto* p : sig.parameters) {
-        hash = HashCombine(hash, p->Type(), p->Usage());
-    }
-    return Hash(hash, sig.return_type);
-}
-
-}  // namespace std
diff --git a/src/tint/lang/wgsl/sem/call_target.h b/src/tint/lang/wgsl/sem/call_target.h
index cdf7141..24ed2ae 100644
--- a/src/tint/lang/wgsl/sem/call_target.h
+++ b/src/tint/lang/wgsl/sem/call_target.h
@@ -54,10 +54,8 @@
     /// Destructor
     ~CallTargetSignature();
 
-    /// The type of the call target return value
-    const core::type::Type* return_type = nullptr;
-    /// The parameters of the call target
-    tint::Vector<const sem::Parameter*, 8> parameters;
+    /// @returns the hash code of the CallTargetSignature
+    tint::HashCode HashCode() const;
 
     /// Equality operator
     /// @param other the signature to compare this to
@@ -76,6 +74,12 @@
         auto idx = IndexOf(usage);
         return (idx >= 0) ? parameters[static_cast<size_t>(idx)] : nullptr;
     }
+
+    /// The type of the call target return value
+    const core::type::Type* return_type = nullptr;
+
+    /// The parameters of the call target
+    tint::Vector<const sem::Parameter*, 8> parameters;
 };
 
 /// CallTarget is the base for callable functions, builtins, value constructors and value
@@ -140,19 +144,4 @@
 
 }  // namespace tint::sem
 
-namespace std {
-
-/// Custom std::hash specialization for tint::sem::CallTargetSignature so
-/// CallTargetSignature can be used as keys for std::unordered_map and
-/// std::unordered_set.
-template <>
-class hash<tint::sem::CallTargetSignature> {
-  public:
-    /// @param sig the CallTargetSignature to hash
-    /// @return the hash value
-    std::size_t operator()(const tint::sem::CallTargetSignature& sig) const;
-};
-
-}  // namespace std
-
 #endif  // SRC_TINT_LANG_WGSL_SEM_CALL_TARGET_H_
diff --git a/src/tint/lang/wgsl/sem/value_expression_test.cc b/src/tint/lang/wgsl/sem/value_expression_test.cc
index c3ced06..de31e95 100644
--- a/src/tint/lang/wgsl/sem/value_expression_test.cc
+++ b/src/tint/lang/wgsl/sem/value_expression_test.cc
@@ -46,7 +46,7 @@
     size_t NumElements() const override { return 0; }
     bool AllZero() const override { return {}; }
     bool AnyZero() const override { return {}; }
-    size_t Hash() const override { return 0; }
+    HashCode Hash() const override { return 0; }
     MockConstant* Clone(core::constant::CloneContext&) const override { return nullptr; }
 
   protected:
diff --git a/src/tint/utils/containers/enum_set.h b/src/tint/utils/containers/enum_set.h
index 611a314..5736c13 100644
--- a/src/tint/utils/containers/enum_set.h
+++ b/src/tint/utils/containers/enum_set.h
@@ -33,6 +33,7 @@
 #include <type_traits>
 #include <utility>
 
+#include "src/tint/utils/math/hash.h"
 #include "src/tint/utils/traits/traits.h"
 
 namespace tint {
@@ -148,7 +149,7 @@
     inline bool Empty() const { return set == 0; }
 
     /// @return the hash value of this object
-    inline size_t HashCode() const { return std::hash<uint64_t>()(Value()); }
+    tint::HashCode HashCode() const { return Hash(Value()); }
 
     /// Equality operator
     /// @param rhs the other EnumSet to compare this to
diff --git a/src/tint/utils/containers/hashmap.h b/src/tint/utils/containers/hashmap.h
index 58f653d..69790f3 100644
--- a/src/tint/utils/containers/hashmap.h
+++ b/src/tint/utils/containers/hashmap.h
@@ -331,7 +331,7 @@
 struct Hasher<Hashmap<K, V, N, HASH, EQUAL>> {
     /// @param map the Hashmap to hash
     /// @returns a hash of the map
-    size_t operator()(const Hashmap<K, V, N, HASH, EQUAL>& map) const {
+    HashCode operator()(const Hashmap<K, V, N, HASH, EQUAL>& map) const {
         auto hash = Hash(map.Count());
         for (auto it : map) {
             // Use an XOR to ensure that the non-deterministic ordering of the map still produces
diff --git a/src/tint/utils/containers/hashmap_base.h b/src/tint/utils/containers/hashmap_base.h
index 1697959..2a86e3a 100644
--- a/src/tint/utils/containers/hashmap_base.h
+++ b/src/tint/utils/containers/hashmap_base.h
@@ -75,12 +75,12 @@
     /// Constructor using pre-computed hash and copied value.
     /// @param hash_ the precomputed hash of @p value
     /// @param value the key value
-    HashmapKey(size_t hash_, const T& value) : value_(value), hash(hash_) {}
+    HashmapKey(HashCode hash_, const T& value) : value_(value), hash(hash_) {}
 
     /// Constructor using pre-computed hash and moved value.
     /// @param hash_ the precomputed hash of @p value
     /// @param value the key value
-    HashmapKey(size_t hash_, T&& value) : value_(std::forward<T>(value)), hash(hash_) {}
+    HashmapKey(HashCode hash_, T&& value) : value_(std::forward<T>(value)), hash(hash_) {}
 
     /// Copy constructor
     HashmapKey(const HashmapKey&) = default;
@@ -145,7 +145,7 @@
     }
 
     /// The hash of value
-    const size_t hash;
+    const HashCode hash;
 };
 
 /// Writes the HashmapKey to the stream.
@@ -298,7 +298,7 @@
     /// the map is cleared, or the map is destructed.
     template <typename K>
     Entry* GetEntry(K&& key) {
-        size_t hash = Hash{}(key);
+        HashCode hash = Hash{}(key);
         auto& slot = slots_[hash % slots_.Length()];
         return slot.Find(hash, key);
     }
@@ -310,7 +310,7 @@
     /// the map is cleared, or the map is destructed.
     template <typename K>
     const Entry* GetEntry(K&& key) const {
-        size_t hash = Hash{}(key);
+        HashCode hash = Hash{}(key);
         auto& slot = slots_[hash % slots_.Length()];
         return slot.Find(hash, key);
     }
@@ -327,7 +327,7 @@
     /// @param key the key to look for.
     template <typename K = Key>
     bool Remove(K&& key) {
-        size_t hash = Hash{}(key);
+        HashCode hash = Hash{}(key);
         auto& slot = slots_[hash % slots_.Length()];
         Node** edge = &slot.nodes;
         for (auto* node = *edge; node; node = node->next) {
@@ -442,7 +442,7 @@
         /// @returns true if the Entry's hash is equal to @p hash, and the Entry's key is equal to
         /// @p value.
         template <typename T>
-        bool Equals(size_t hash, T&& value) const {
+        bool Equals(HashCode hash, T&& value) const {
             auto& key = Key();
             return key.hash == hash && HashmapBase::Equal{}(key.Value(), value);
         }
@@ -497,7 +497,7 @@
         /// The slot that will hold the edit.
         Slot& slot;
         /// The hash of the key, passed to EditAt().
-        size_t hash;
+        HashCode hash;
         /// The resolved node entry, or nullptr if EditAt() did not resolve to an existing entry.
         Entry* entry = nullptr;
 
@@ -540,7 +540,7 @@
             capacity_ += capacity_;
             Rehash();
         }
-        size_t hash = Hash{}(key);
+        HashCode hash = Hash{}(key);
         auto& slot = slots_[hash % slots_.Length()];
         auto* entry = slot.Find(hash, key);
         return {*this, slot, hash, entry};
@@ -582,7 +582,7 @@
         /// @param hash the key hash to search for.
         /// @param key the key value to search for.
         template <typename K>
-        const Entry* Find(size_t hash, K&& key) const {
+        const Entry* Find(HashCode hash, K&& key) const {
             for (auto* node = nodes; node; node = node->next) {
                 if (node->Equals(hash, key)) {
                     return &node->Entry();
@@ -595,7 +595,7 @@
         /// @param hash the key hash to search for.
         /// @param key the key value to search for.
         template <typename K>
-        Entry* Find(size_t hash, K&& key) {
+        Entry* Find(HashCode hash, K&& key) {
             for (auto* node = nodes; node; node = node->next) {
                 if (node->Equals(hash, key)) {
                     return &node->Entry();
diff --git a/src/tint/utils/containers/unique_allocator.h b/src/tint/utils/containers/unique_allocator.h
index 8d88623..b6d7224 100644
--- a/src/tint/utils/containers/unique_allocator.h
+++ b/src/tint/utils/containers/unique_allocator.h
@@ -37,7 +37,7 @@
 namespace tint {
 
 /// UniqueAllocator is used to allocate unique instances of the template type `T`.
-template <typename T, typename HASH = std::hash<T>, typename EQUAL = std::equal_to<T>>
+template <typename T, typename HASH = Hasher<T>, typename EQUAL = std::equal_to<T>>
 class UniqueAllocator {
   public:
     /// Iterator is the type returned by begin() and end()
@@ -90,7 +90,7 @@
         /// Hashing function
         /// @param e the entry
         /// @returns the hash of the entry
-        size_t operator()(T* e) const { return HASH{}(*e); }
+        HashCode operator()(T* e) const { return HASH{}(*e); }
     };
 
     /// Equality is the equality function used by the Hashset
diff --git a/src/tint/utils/containers/unique_vector.h b/src/tint/utils/containers/unique_vector.h
index bf59b06..38e4714 100644
--- a/src/tint/utils/containers/unique_vector.h
+++ b/src/tint/utils/containers/unique_vector.h
@@ -41,7 +41,7 @@
 
 /// UniqueVector is an ordered container that only contains unique items.
 /// Attempting to add a duplicate is a no-op.
-template <typename T, size_t N, typename HASH = std::hash<T>, typename EQUAL = std::equal_to<T>>
+template <typename T, size_t N, typename HASH = Hasher<T>, typename EQUAL = std::equal_to<T>>
 struct UniqueVector {
     /// STL-friendly alias to T. Used by gmock.
     using value_type = T;
diff --git a/src/tint/utils/containers/vector.h b/src/tint/utils/containers/vector.h
index 663e310..c7edd0e 100644
--- a/src/tint/utils/containers/vector.h
+++ b/src/tint/utils/containers/vector.h
@@ -777,7 +777,7 @@
 #endif
 
     /// @returns a hash code for this Vector
-    size_t HashCode() const {
+    tint::HashCode HashCode() const {
         auto hash = Hash(Length());
         for (auto& el : *this) {
             hash = HashCombine(hash, el);
@@ -1160,7 +1160,7 @@
     auto rend() const { return slice_.rend(); }
 
     /// @returns a hash code of the Vector
-    size_t HashCode() const {
+    tint::HashCode HashCode() const {
         auto hash = Hash(Length());
         for (auto& el : *this) {
             hash = HashCombine(hash, el);
diff --git a/src/tint/utils/math/hash.h b/src/tint/utils/math/hash.h
index 43c97f4..a148f2c 100644
--- a/src/tint/utils/math/hash.h
+++ b/src/tint/utils/math/hash.h
@@ -40,39 +40,9 @@
 #include "src/tint/utils/math/crc32.h"
 
 namespace tint {
+
 namespace detail {
 
-/// Helper for obtaining a seed bias value for HashCombine with a bit-width
-/// dependent on the size of size_t.
-template <int SIZE_OF_SIZE_T>
-struct HashCombineOffset {};
-
-/// Specialization of HashCombineOffset for size_t == 4.
-template <>
-struct HashCombineOffset<4> {
-    /// @returns the seed bias value for HashCombine()
-    static constexpr inline uint32_t value() {
-        constexpr uint32_t base = 0x7f4a7c16;
-#ifdef TINT_HASH_SEED
-        return base ^ static_cast<uint32_t>(TINT_HASH_SEED);
-#endif
-        return base;
-    }
-};
-
-/// Specialization of HashCombineOffset for size_t == 8.
-template <>
-struct HashCombineOffset<8> {
-    /// @returns the seed bias value for HashCombine()
-    static constexpr inline uint64_t value() {
-        constexpr uint64_t base = 0x9e3779b97f4a7c16;
-#ifdef TINT_HASH_SEED
-        return base ^ static_cast<uint64_t>(TINT_HASH_SEED);
-#endif
-        return base;
-    }
-};
-
 template <typename T, typename = void>
 struct HasHashCodeMember : std::false_type {};
 
@@ -84,49 +54,53 @@
 
 }  // namespace detail
 
+/// The type of a hash code
+using HashCode = uint32_t;
+
 /// Forward declarations (see below)
 template <typename... ARGS>
-size_t Hash(const ARGS&... values);
+HashCode Hash(const ARGS&... values);
 
 template <typename... ARGS>
-size_t HashCombine(size_t hash, const ARGS&... values);
+HashCode HashCombine(HashCode hash, const ARGS&... values);
 
 /// A STL-compatible hasher that does a more thorough job than most implementations of std::hash.
 /// Hasher has been optimized for a better quality hash at the expense of increased computation
 /// costs.
 /// Hasher is specialized for various core Tint data types. The default implementation will use a
-/// `size_t HashCode()` method on the `T` type, and will fallback to `std::hash<T>` if
+/// `HashCode HashCode()` method on the `T` type, and will fallback to `std::hash<T>` if
 /// `T::HashCode` is missing.
 template <typename T>
 struct Hasher {
     /// @param value the value to hash
     /// @returns a hash of the value
-    size_t operator()(const T& value) const {
+    HashCode operator()(const T& value) const {
         if constexpr (detail::HasHashCodeMember<T>::value) {
             auto hash = value.HashCode();
-            static_assert(std::is_same_v<decltype(hash), size_t>,
-                          "T::HashCode() must return size_t");
+            static_assert(std::is_same_v<decltype(hash), HashCode>,
+                          "T::HashCode() must return HashCode");
             return hash;
         } else {
-            return std::hash<T>()(value);
+            return static_cast<HashCode>(std::hash<T>()(value));
         }
     }
 };
 
 /// Hasher specialization for pointers
-/// std::hash<T*> typically uses a reinterpret of the pointer to a size_t.
-/// As most pointers a 4 or 16 byte aligned, this usually results in the LSBs of the hash being 0,
-/// resulting in bad hashes for hashtables. This implementation mixes up those LSBs.
 template <typename T>
 struct Hasher<T*> {
     /// @param ptr the pointer to hash
     /// @returns a hash of the pointer
-    size_t operator()(T* ptr) const {
-        auto hash = static_cast<size_t>(reinterpret_cast<uintptr_t>(ptr));
+    HashCode operator()(T* ptr) const {
+        auto hash = reinterpret_cast<uintptr_t>(ptr);
 #ifdef TINT_HASH_SEED
         hash ^= static_cast<uint32_t>(TINT_HASH_SEED);
 #endif
-        return hash >> 4;
+        if constexpr (sizeof(hash) > 4) {
+            return static_cast<HashCode>(hash >> 4 | hash >> 32);
+        } else {
+            return static_cast<HashCode>(hash >> 4);
+        }
     }
 };
 
@@ -135,7 +109,7 @@
 struct Hasher<std::vector<T>> {
     /// @param vector the vector to hash
     /// @returns a hash of the vector
-    size_t operator()(const std::vector<T>& vector) const {
+    HashCode operator()(const std::vector<T>& vector) const {
         auto hash = Hash(vector.size());
         for (auto& el : vector) {
             hash = HashCombine(hash, el);
@@ -149,7 +123,7 @@
 struct Hasher<std::tuple<TYPES...>> {
     /// @param tuple the tuple to hash
     /// @returns a hash of the tuple
-    size_t operator()(const std::tuple<TYPES...>& tuple) const {
+    HashCode operator()(const std::tuple<TYPES...>& tuple) const {
         return std::apply(Hash<TYPES...>, tuple);
     }
 };
@@ -159,7 +133,9 @@
 struct Hasher<std::pair<A, B>> {
     /// @param tuple the tuple to hash
     /// @returns a hash of the tuple
-    size_t operator()(const std::pair<A, B>& tuple) const { return std::apply(Hash<A, B>, tuple); }
+    HashCode operator()(const std::pair<A, B>& tuple) const {
+        return std::apply(Hash<A, B>, tuple);
+    }
 };
 
 /// Hasher specialization for std::variant
@@ -167,7 +143,7 @@
 struct Hasher<std::variant<TYPES...>> {
     /// @param variant the variant to hash
     /// @returns a hash of the tuple
-    size_t operator()(const std::variant<TYPES...>& variant) const {
+    HashCode operator()(const std::variant<TYPES...>& variant) const {
         return std::visit([](auto&& val) { return Hash(val); }, variant);
     }
 };
@@ -178,20 +154,20 @@
 struct Hasher<std::string> {
     /// @param str the string to hash
     /// @returns a hash of the string
-    size_t operator()(const std::string& str) const {
-        return std::hash<std::string_view>()(std::string_view(str));
+    HashCode operator()(const std::string& str) const {
+        return static_cast<HashCode>(std::hash<std::string_view>()(std::string_view(str)));
     }
 
     /// @param str the string to hash
     /// @returns a hash of the string
-    size_t operator()(const char* str) const {
-        return std::hash<std::string_view>()(std::string_view(str));
+    HashCode operator()(const char* str) const {
+        return static_cast<HashCode>(std::hash<std::string_view>()(std::string_view(str)));
     }
 
     /// @param str the string to hash
     /// @returns a hash of the string
-    size_t operator()(const std::string_view& str) const {
-        return std::hash<std::string_view>()(str);
+    HashCode operator()(const std::string_view& str) const {
+        return static_cast<HashCode>(std::hash<std::string_view>()(str));
     }
 };
 
@@ -199,14 +175,14 @@
 /// @returns a hash of the variadic list of arguments.
 ///          The returned hash is dependent on the order of the arguments.
 template <typename... ARGS>
-size_t Hash(const ARGS&... args) {
+HashCode Hash(const ARGS&... args) {
     if constexpr (sizeof...(ARGS) == 0) {
         return 0;
     } else if constexpr (sizeof...(ARGS) == 1) {
         using T = std::tuple_element_t<0, std::tuple<ARGS...>>;
         return Hasher<T>()(args...);
     } else {
-        size_t hash = 102931;  // seed with an arbitrary prime
+        HashCode hash = 102931;  // seed with an arbitrary prime
         return HashCombine(hash, args...);
     }
 }
@@ -216,8 +192,13 @@
 /// @returns a hash of the variadic list of arguments.
 ///          The returned hash is dependent on the order of the arguments.
 template <typename... ARGS>
-size_t HashCombine(size_t hash, const ARGS&... values) {
-    constexpr size_t offset = tint::detail::HashCombineOffset<sizeof(size_t)>::value();
+HashCode HashCombine(HashCode hash, const ARGS&... values) {
+#ifdef TINT_HASH_SEED
+    constexpr uint32_t offset = 0x7f4a7c16 ^ static_cast<uint32_t>(TINT_HASH_SEED);
+#else
+    constexpr uint32_t offset = 0x7f4a7c16;
+#endif
+
     ((hash ^= Hash(values) + (offset ^ (hash >> 2))), ...);
     return hash;
 }
@@ -270,7 +251,7 @@
     /// The wrapped value
     T value;
     /// The hash of value
-    size_t hash;
+    HashCode hash;
 
     /// Constructor
     /// @param v the value to wrap
diff --git a/src/tint/utils/rtti/castable.h b/src/tint/utils/rtti/castable.h
index 0683254..c944074 100644
--- a/src/tint/utils/rtti/castable.h
+++ b/src/tint/utils/rtti/castable.h
@@ -34,7 +34,9 @@
 #include <type_traits>
 #include <utility>
 
+#include "src/tint/utils/macros/compiler.h"
 #include "src/tint/utils/math/crc32.h"
+#include "src/tint/utils/math/hash.h"
 #include "src/tint/utils/rtti/ignore.h"
 #include "src/tint/utils/traits/traits.h"
 
@@ -43,15 +45,15 @@
 #define TINT_CASTABLE_PUSH_DISABLE_WARNINGS()                                 \
     _Pragma("clang diagnostic push")                                     /**/ \
         _Pragma("clang diagnostic ignored \"-Wundefined-var-template\"") /**/ \
-        static_assert(true, "require extra semicolon")
+        TINT_REQUIRE_SEMICOLON
 
 /// Restore disabled warnings
 #define TINT_CASTABLE_POP_DISABLE_WARNINGS() \
     _Pragma("clang diagnostic pop") /**/     \
-        static_assert(true, "require extra semicolon")
+        TINT_REQUIRE_SEMICOLON
 #else
-#define TINT_CASTABLE_PUSH_DISABLE_WARNINGS() static_assert(true, "require extra semicolon")
-#define TINT_CASTABLE_POP_DISABLE_WARNINGS() static_assert(true, "require extra semicolon")
+#define TINT_CASTABLE_PUSH_DISABLE_WARNINGS() TINT_REQUIRE_SEMICOLON
+#define TINT_CASTABLE_POP_DISABLE_WARNINGS() TINT_REQUIRE_SEMICOLON
 #endif
 
 TINT_CASTABLE_PUSH_DISABLE_WARNINGS();