diff --git a/llvm/include/llvm/Support/HashBuilder.h b/llvm/include/llvm/Support/HashBuilder.h --- a/llvm/include/llvm/Support/HashBuilder.h +++ b/llvm/include/llvm/Support/HashBuilder.h @@ -16,6 +16,7 @@ #define LLVM_SUPPORT_HASHBUILDER_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Hashing.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Endian.h" @@ -26,6 +27,15 @@ namespace llvm { +namespace hashbuilder_detail { +/// Trait to indicate whether a type's bits can be hashed directly (after +/// endianness correction). +template +struct IsHashableData + : std::integral_constant::value> {}; + +} // namespace hashbuilder_detail + /// Declares the hasher member, and functions forwarding directly to the hasher. template class HashBuilderBase { public: @@ -78,15 +88,10 @@ /// `support::endianness::big` or `support::endianness::little`. template class HashBuilderImpl : public HashBuilderBase { +public: static_assert(Endianness != support::endianness::native, "HashBuilder should canonicalize endianness"); - /// Trait to indicate whether a type's bits can be hashed directly (after - /// endianness correction). - template - struct IsHashableData - : std::integral_constant::value> {}; -public: explicit HashBuilderImpl(HasherT &Hasher) : HashBuilderBase(Hasher) {} template @@ -95,7 +100,9 @@ /// Implement hashing for hashable data types, e.g. integral or enum values. template - std::enable_if_t::value, HashBuilderImpl &> add(T Value) { + std::enable_if_t::value, + HashBuilderImpl &> + add(T Value) { return adjustForEndiannessAndAdd(Value); } @@ -118,7 +125,7 @@ // details of `ArrayRef::begin()` and `ArrayRef::end()`. Explicitly call // `update` to guarantee the fast path. add(Value.size()); - if (IsHashableData::value && + if (hashbuilder_detail::IsHashableData::value && Endianness == support::endian::system_endianness()) { this->update( makeArrayRef(reinterpret_cast(Value.begin()), @@ -242,7 +249,7 @@ /// ``` template std::enable_if_t::value && - !IsHashableData::value, + !hashbuilder_detail::IsHashableData::value, HashBuilderImpl &> add(const T &Value) { addHash(*this, Value); @@ -334,7 +341,7 @@ } template - std::enable_if_t::value && + std::enable_if_t::value && Endianness == support::endian::system_endianness(), HashBuilderImpl &> addRangeElementsImpl(T *First, T *Last, std::forward_iterator_tag) { @@ -399,6 +406,33 @@ HashBuilderImpl; + +class HashCodeHasher { +public: + HashCodeHasher() : Code(0) {} + void update(ArrayRef Data) { + hash_code DataCode = hash_value(Data); + Code = hash_combine(Code, DataCode); + } + hash_code Code; +}; + +namespace hashbuilder_detail { +/// Provide a default implementation of `hash_value` when `addHash(const T &)` +/// is supported. +using HashCodeHashBuilder = + HashBuilder; +} // namespace hashbuilder_detail + +template +std::enable_if_t< + is_detected::value, + hash_code> +hash_value(const T &Value) { + hashbuilder_detail::HashCodeHashBuilder HBuilder; + HBuilder.add(Value); + return HBuilder.getHasher().Code; +} } // end namespace llvm #endif // LLVM_SUPPORT_HASHBUILDER_H diff --git a/llvm/unittests/ADT/HashingTest.cpp b/llvm/unittests/ADT/HashingTest.cpp --- a/llvm/unittests/ADT/HashingTest.cpp +++ b/llvm/unittests/ADT/HashingTest.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/Hashing.h" #include "llvm/Support/DataTypes.h" +#include "llvm/Support/HashBuilder.h" #include "gtest/gtest.h" #include #include @@ -402,4 +403,36 @@ #undef CHECK_SAME } +struct StructWithHashBuilderSupport { + char C; + int I; + template + friend void addHash(llvm::HashBuilderImpl &HBuilder, + const StructWithHashBuilderSupport &Value) { + HBuilder.add(Value.C, Value.I); + } +}; + +TEST(HashingTest, HashWithHashBuilder) { + StructWithHashBuilderSupport S{'c', 1}; + EXPECT_NE(static_cast(llvm::hash_value(S)), static_cast(0)); } + +struct StructWithHashBuilderAndHashValueSupport { + char C; + int I; + template + friend void addHash(llvm::HashBuilderImpl &HBuilder, + const StructWithHashBuilderAndHashValueSupport &Value) {} + friend hash_code + hash_value(const StructWithHashBuilderAndHashValueSupport &Value) { + return 0xbeef; + } +}; + +TEST(HashingTest, HashBuilderAndHashValue) { + StructWithHashBuilderAndHashValueSupport S{'c', 1}; + EXPECT_EQ(static_cast(hash_value(S)), static_cast(0xbeef)); +} + +} // namespace