[tint] Simplify custom hash-code implementations
Instead of requiring a tint::Hasher<T> specialization, look for a
HashCode() method on T. This is far easier to implement than having to
drop out of the current namespace and into the `::tint` namespace.
Change-Id: Idee33a61332be740379e4f6fff356be388dd5565
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/152401
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
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 28db994..9cdc734 100644
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
+++ b/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
@@ -43,6 +43,8 @@
using namespace tint::core::number_suffixes; // NOLINT
using namespace tint::core::fluent_types; // NOLINT
+namespace tint::ast::transform {
+
namespace {
/// AccessRoot describes the root of an AccessShape.
@@ -56,6 +58,9 @@
tint::sem::Variable const* variable = nullptr;
/// The address space of the variable or pointer type.
tint::core::AddressSpace address_space = tint::core::AddressSpace::kUndefined;
+
+ /// @return a hash code for this object
+ size_t HashCode() const { return Hash(type, variable); }
};
/// Inequality operator for AccessRoot
@@ -68,6 +73,9 @@
struct DynamicIndex {
/// The index of the expression in DirectVariableAccess::State::AccessChain::dynamic_indices
size_t slot = 0;
+
+ /// @return a hash code for this object
+ size_t HashCode() const { return Hash(slot); }
};
/// Inequality operator for DynamicIndex
@@ -140,6 +148,9 @@
}
return count;
}
+
+ /// @return a hash code for this object
+ size_t HashCode() const { return Hash(root, ops); }
};
/// Equality operator for AccessShape
@@ -156,46 +167,13 @@
struct AccessChain : AccessShape {
/// The array accessor index expressions. This vector is indexed by the `DynamicIndex`s in
/// #indices.
- tint::Vector<const tint::sem::ValueExpression*, 8> dynamic_indices;
+ Vector<const sem::ValueExpression*, 8> dynamic_indices;
/// If true, then this access chain is used as an argument to call a variant.
bool used_in_call = false;
};
} // namespace
-namespace tint {
-
-/// Hasher specialization for AccessRoot
-template <>
-struct Hasher<AccessRoot> {
- /// The hash function for the AccessRoot
- /// @param d the AccessRoot to hash
- /// @return the hash for the given AccessRoot
- size_t operator()(const AccessRoot& d) const { return Hash(d.type, d.variable); }
-};
-
-/// Hasher specialization for DynamicIndex
-template <>
-struct Hasher<DynamicIndex> {
- /// The hash function for the DynamicIndex
- /// @param d the DynamicIndex to hash
- /// @return the hash for the given DynamicIndex
- size_t operator()(const DynamicIndex& d) const { return Hash(d.slot); }
-};
-
-/// Hasher specialization for AccessShape
-template <>
-struct Hasher<AccessShape> {
- /// The hash function for the AccessShape
- /// @param s the AccessShape to hash
- /// @return the hash for the given AccessShape
- size_t operator()(const AccessShape& s) const { return Hash(s.root, s.ops); }
-};
-
-} // namespace tint
-
-namespace tint::ast::transform {
-
/// The PIMPL state for the DirectVariableAccess transform
struct DirectVariableAccess::State {
/// Constructor
diff --git a/src/tint/lang/wgsl/ast/transform/remove_phonies.cc b/src/tint/lang/wgsl/ast/transform/remove_phonies.cc
index e50b3b6..4a6a392 100644
--- a/src/tint/lang/wgsl/ast/transform/remove_phonies.cc
+++ b/src/tint/lang/wgsl/ast/transform/remove_phonies.cc
@@ -49,7 +49,7 @@
auto& sem = src->Sem();
- Hashmap<SinkSignature, Symbol, 8, Hasher<SinkSignature>> sinks;
+ Hashmap<SinkSignature, Symbol, 8> sinks;
bool made_changes = false;
for (auto* node : src->ASTNodes().Objects()) {
diff --git a/src/tint/lang/wgsl/ast/transform/std140.cc b/src/tint/lang/wgsl/ast/transform/std140.cc
index 3d41108..fd3193c 100644
--- a/src/tint/lang/wgsl/ast/transform/std140.cc
+++ b/src/tint/lang/wgsl/ast/transform/std140.cc
@@ -38,10 +38,14 @@
using namespace tint::core::number_suffixes; // NOLINT
using namespace tint::core::fluent_types; // NOLINT
+namespace tint::ast::transform {
namespace {
/// UniformVariable is used by Std140::State::AccessIndex to indicate the root uniform variable
-struct UniformVariable {};
+struct UniformVariable {
+ /// @returns a hash code for this object
+ size_t HashCode() const { return 0; }
+};
/// Inequality operator for UniformVariable
bool operator!=(const UniformVariable&, const UniformVariable&) {
@@ -51,6 +55,9 @@
/// DynamicIndex is used by Std140::State::AccessIndex to indicate a runtime-expression index
struct DynamicIndex {
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); }
};
/// Inequality operator for DynamicIndex
@@ -60,29 +67,6 @@
} // namespace
-namespace tint {
-
-/// Hasher specialization for UniformVariable
-template <>
-struct Hasher<UniformVariable> {
- /// The hash function for the UniformVariable
- /// @return the hash for the given UniformVariable
- size_t operator()(const UniformVariable&) const { return 0; }
-};
-
-/// Hasher specialization for DynamicIndex
-template <>
-struct Hasher<DynamicIndex> {
- /// The hash function for the DynamicIndex
- /// @param d the DynamicIndex to hash
- /// @return the hash for the given DynamicIndex
- size_t operator()(const DynamicIndex& d) const { return Hash(d.slot); }
-};
-
-} // namespace tint
-
-namespace tint::ast::transform {
-
/// PIMPL state for the transform
struct Std140::State {
/// Constructor
diff --git a/src/tint/lang/wgsl/resolver/validator.h b/src/tint/lang/wgsl/resolver/validator.h
index 8e80916..38bb8f1 100644
--- a/src/tint/lang/wgsl/resolver/validator.h
+++ b/src/tint/lang/wgsl/resolver/validator.h
@@ -82,6 +82,9 @@
bool operator==(const TypeAndAddressSpace& other) const {
return type == other.type && address_space == other.address_space;
}
+
+ /// @returns the hash value of this object
+ std::size_t HashCode() const { return Hash(type, address_space); }
};
/// DiagnosticFilterStack is a scoped stack of diagnostic filters.
@@ -573,19 +576,4 @@
} // namespace tint::resolver
-namespace std {
-
-/// Custom std::hash specialization for tint::resolver::TypeAndAddressSpace.
-template <>
-class hash<tint::resolver::TypeAndAddressSpace> {
- public:
- /// @param tas the TypeAndAddressSpace
- /// @return the hash value
- inline std::size_t operator()(const tint::resolver::TypeAndAddressSpace& tas) const {
- return Hash(tas.type, tas.address_space);
- }
-};
-
-} // namespace std
-
#endif // SRC_TINT_LANG_WGSL_RESOLVER_VALIDATOR_H_
diff --git a/src/tint/lang/wgsl/sem/builtin.h b/src/tint/lang/wgsl/sem/builtin.h
index 1d0ce97..838288a 100644
--- a/src/tint/lang/wgsl/sem/builtin.h
+++ b/src/tint/lang/wgsl/sem/builtin.h
@@ -104,6 +104,11 @@
/// wgsl::Extension::kNone if no extension is required.
wgsl::Extension RequiredExtension() const;
+ /// @return the hash code for this object
+ std::size_t HashCode() const {
+ return Hash(Type(), SupportedStages(), ReturnType(), Parameters(), IsDeprecated());
+ }
+
private:
const core::Function type_;
const PipelineStageSet supported_stages_;
@@ -118,20 +123,4 @@
} // namespace tint::sem
-namespace std {
-
-/// Custom std::hash specialization for tint::sem::Builtin
-template <>
-class hash<tint::sem::Builtin> {
- public:
- /// @param i the Builtin to create a hash for
- /// @return the hash value
- inline std::size_t operator()(const tint::sem::Builtin& i) const {
- return Hash(i.Type(), i.SupportedStages(), i.ReturnType(), i.Parameters(),
- i.IsDeprecated());
- }
-};
-
-} // namespace std
-
#endif // SRC_TINT_LANG_WGSL_SEM_BUILTIN_H_
diff --git a/src/tint/utils/containers/enum_set.h b/src/tint/utils/containers/enum_set.h
index 488a230..5f1c33d 100644
--- a/src/tint/utils/containers/enum_set.h
+++ b/src/tint/utils/containers/enum_set.h
@@ -134,6 +134,9 @@
/// @return true if the set is empty
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()); }
+
/// Equality operator
/// @param rhs the other EnumSet to compare this to
/// @return true if this EnumSet is equal to @p rhs
@@ -243,19 +246,4 @@
} // namespace tint
-namespace std {
-
-/// Custom std::hash specialization for tint::EnumSet<T>
-template <typename T>
-class hash<tint::EnumSet<T>> {
- public:
- /// @param e the EnumSet to create a hash for
- /// @return the hash value
- inline std::size_t operator()(const tint::EnumSet<T>& e) const {
- return std::hash<uint64_t>()(e.Value());
- }
-};
-
-} // namespace std
-
#endif // SRC_TINT_UTILS_CONTAINERS_ENUM_SET_H_
diff --git a/src/tint/utils/containers/enum_set_test.cc b/src/tint/utils/containers/enum_set_test.cc
index dbeb54c..c27e499 100644
--- a/src/tint/utils/containers/enum_set_test.cc
+++ b/src/tint/utils/containers/enum_set_test.cc
@@ -202,9 +202,8 @@
EXPECT_TRUE(EnumSet<E>(E::A, E::B) != E::C);
}
-TEST(EnumSetTest, Hash) {
- auto hash = [&](EnumSet<E> s) { return std::hash<EnumSet<E>>()(s); };
- EXPECT_EQ(hash(EnumSet<E>(E::A, E::B)), hash(EnumSet<E>(E::A, E::B)));
+TEST(EnumSetTest, HashCode) {
+ EXPECT_EQ(EnumSet<E>(E::A, E::B).HashCode(), EnumSet<E>(E::A, E::B).HashCode());
}
TEST(EnumSetTest, Value) {
diff --git a/src/tint/utils/containers/hashmap.h b/src/tint/utils/containers/hashmap.h
index d3c7b96..3fcfc01 100644
--- a/src/tint/utils/containers/hashmap.h
+++ b/src/tint/utils/containers/hashmap.h
@@ -279,7 +279,7 @@
for (auto it : map) {
// Use an XOR to ensure that the non-deterministic ordering of the map still produces
// the same hash value for the same entries.
- hash ^= Hash(it.key) * 31 + Hash(it.value);
+ hash ^= Hash(it.key, it.value);
}
return hash;
}
diff --git a/src/tint/utils/containers/vector.h b/src/tint/utils/containers/vector.h
index e2e4f67..d2f2c69 100644
--- a/src/tint/utils/containers/vector.h
+++ b/src/tint/utils/containers/vector.h
@@ -441,6 +441,15 @@
/// @returns the end for a reverse iterator
auto rend() const { return impl_.slice.rend(); }
+ /// @returns a hash code for this Vector
+ size_t HashCode() const {
+ auto hash = Hash(Length());
+ for (auto& el : *this) {
+ hash = HashCombine(hash, el);
+ }
+ return hash;
+ }
+
/// Equality operator
/// @param other the other vector
/// @returns true if this vector is the same length as `other`, and all elements are equal.
@@ -775,6 +784,15 @@
/// @returns the end for a reverse iterator
auto rend() const { return slice_.rend(); }
+ /// @returns a hash code of the Vector
+ size_t HashCode() const {
+ auto hash = Hash(Length());
+ for (auto& el : *this) {
+ hash = HashCombine(hash, el);
+ }
+ return hash;
+ }
+
private:
/// Friend class
template <typename, size_t>
@@ -860,34 +878,6 @@
return o;
}
-/// Hasher specialization for Vector
-template <typename T, size_t N>
-struct Hasher<Vector<T, N>> {
- /// @param vector the Vector to hash
- /// @returns a hash of the Vector
- size_t operator()(const Vector<T, N>& vector) const {
- auto hash = Hash(vector.Length());
- for (auto& el : vector) {
- hash = HashCombine(hash, el);
- }
- return hash;
- }
-};
-
-/// Hasher specialization for VectorRef
-template <typename T>
-struct Hasher<VectorRef<T>> {
- /// @param vector the VectorRef reference to hash
- /// @returns a hash of the Vector
- size_t operator()(const VectorRef<T>& vector) const {
- auto hash = Hash(vector.Length());
- for (auto& el : vector) {
- hash = HashCombine(hash, el);
- }
- return hash;
- }
-};
-
namespace detail {
/// IsVectorLike<T>::value is true if T is a Vector or VectorRef.
diff --git a/src/tint/utils/math/hash.h b/src/tint/utils/math/hash.h
index 2fe6fee..38d9c92 100644
--- a/src/tint/utils/math/hash.h
+++ b/src/tint/utils/math/hash.h
@@ -60,6 +60,15 @@
}
};
+template <typename T, typename = void>
+struct HasHashCodeMember : std::false_type {};
+
+template <typename T>
+struct HasHashCodeMember<
+ T,
+ std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::HashCode)>>> : std::true_type {
+};
+
} // namespace detail
/// Forward declarations (see below)
@@ -72,11 +81,20 @@
/// 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
+/// `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 { return std::hash<T>()(value); }
+ size_t operator()(const T& value) const {
+ if constexpr (detail::HasHashCodeMember<T>::value) {
+ return value.HashCode();
+ } else {
+ return std::hash<T>()(value);
+ }
+ }
};
/// Hasher specialization for pointers