This revision was landed with ongoing or failed builds. This revision was automatically updated to reflect the committed changes. Closed by commit rG4c484f11d355: [llvm] Add a SFINAE template parameter to DenseMapInfo (authored by rriddle).
Changed prior to commit: https://reviews.llvm.org/D113641?vs=387386&id=387702#toc Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D113641/new/ https://reviews.llvm.org/D113641 Files: clang/include/clang/AST/TypeOrdering.h clang/include/clang/Basic/SourceLocation.h clang/include/clang/Sema/Sema.h lldb/include/lldb/Utility/ConstString.h llvm/include/llvm/ADT/APInt.h llvm/include/llvm/ADT/APSInt.h llvm/include/llvm/ADT/ArrayRef.h llvm/include/llvm/ADT/DenseMapInfo.h llvm/include/llvm/ADT/Hashing.h llvm/include/llvm/ADT/ImmutableList.h llvm/include/llvm/ADT/PointerIntPair.h llvm/include/llvm/ADT/StringRef.h llvm/include/llvm/BinaryFormat/WasmTraits.h llvm/include/llvm/CodeGen/SelectionDAGNodes.h llvm/include/llvm/IR/Attributes.h llvm/include/llvm/Support/TypeSize.h llvm/lib/Support/APInt.cpp llvm/unittests/ADT/DenseMapTest.cpp mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.h mlir/include/mlir/IR/Attributes.h mlir/include/mlir/IR/BuiltinOps.h mlir/include/mlir/IR/OpDefinition.h mlir/include/mlir/IR/Types.h mlir/include/mlir/Support/LLVM.h
Index: mlir/include/mlir/Support/LLVM.h =================================================================== --- mlir/include/mlir/Support/LLVM.h +++ mlir/include/mlir/Support/LLVM.h @@ -46,7 +46,7 @@ } // namespace detail template <typename KeyT, typename ValueT, typename KeyInfoT, typename BucketT> class DenseMap; -template <typename T> struct DenseMapInfo; +template <typename T, typename Enable> struct DenseMapInfo; template <typename ValueT, typename ValueInfoT> class DenseSet; class MallocAllocator; template <typename T> class MutableArrayRef; @@ -90,7 +90,8 @@ // // Containers. using llvm::ArrayRef; -using llvm::DenseMapInfo; +template <typename T, typename Enable = void> +using DenseMapInfo = llvm::DenseMapInfo<T, Enable>; template <typename KeyT, typename ValueT, typename KeyInfoT = DenseMapInfo<KeyT>, typename BucketT = llvm::detail::DenseMapPair<KeyT, ValueT>> Index: mlir/include/mlir/IR/Types.h =================================================================== --- mlir/include/mlir/IR/Types.h +++ mlir/include/mlir/IR/Types.h @@ -269,6 +269,18 @@ static unsigned getHashValue(mlir::Type val) { return mlir::hash_value(val); } static bool isEqual(mlir::Type LHS, mlir::Type RHS) { return LHS == RHS; } }; +template <typename T> +struct DenseMapInfo<T, std::enable_if_t<std::is_base_of<mlir::Type, T>::value>> + : public DenseMapInfo<mlir::Type> { + static T getEmptyKey() { + const void *pointer = llvm::DenseMapInfo<const void *>::getEmptyKey(); + return T::getFromOpaquePointer(pointer); + } + static T getTombstoneKey() { + const void *pointer = llvm::DenseMapInfo<const void *>::getTombstoneKey(); + return T::getFromOpaquePointer(pointer); + } +}; /// We align TypeStorage by 8, so allow LLVM to steal the low bits. template <> struct PointerLikeTypeTraits<mlir::Type> { Index: mlir/include/mlir/IR/OpDefinition.h =================================================================== --- mlir/include/mlir/IR/OpDefinition.h +++ mlir/include/mlir/IR/OpDefinition.h @@ -1906,4 +1906,25 @@ } // namespace impl } // end namespace mlir +namespace llvm { + +template <typename T> +struct DenseMapInfo< + T, std::enable_if_t<std::is_base_of<mlir::OpState, T>::value>> { + static inline T getEmptyKey() { + auto *pointer = llvm::DenseMapInfo<void *>::getEmptyKey(); + return T::getFromOpaquePointer(pointer); + } + static inline T getTombstoneKey() { + auto *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey(); + return T::getFromOpaquePointer(pointer); + } + static unsigned getHashValue(T val) { + return hash_value(val.getAsOpaquePointer()); + } + static bool isEqual(T lhs, T rhs) { return lhs == rhs; } +}; + +} // end namespace llvm + #endif Index: mlir/include/mlir/IR/BuiltinOps.h =================================================================== --- mlir/include/mlir/IR/BuiltinOps.h +++ mlir/include/mlir/IR/BuiltinOps.h @@ -49,23 +49,6 @@ } // end namespace mlir namespace llvm { -// Functions hash just like pointers. -template <> -struct DenseMapInfo<mlir::FuncOp> { - static mlir::FuncOp getEmptyKey() { - auto *pointer = llvm::DenseMapInfo<void *>::getEmptyKey(); - return mlir::FuncOp::getFromOpaquePointer(pointer); - } - static mlir::FuncOp getTombstoneKey() { - auto *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey(); - return mlir::FuncOp::getFromOpaquePointer(pointer); - } - static unsigned getHashValue(mlir::FuncOp val) { - return hash_value(val.getAsOpaquePointer()); - } - static bool isEqual(mlir::FuncOp lhs, mlir::FuncOp rhs) { return lhs == rhs; } -}; - /// Allow stealing the low bits of FuncOp. template <> struct PointerLikeTypeTraits<mlir::FuncOp> { Index: mlir/include/mlir/IR/Attributes.h =================================================================== --- mlir/include/mlir/IR/Attributes.h +++ mlir/include/mlir/IR/Attributes.h @@ -201,6 +201,19 @@ return LHS == RHS; } }; +template <typename T> +struct DenseMapInfo< + T, std::enable_if_t<std::is_base_of<mlir::Attribute, T>::value>> + : public DenseMapInfo<mlir::Attribute> { + static T getEmptyKey() { + const void *pointer = llvm::DenseMapInfo<const void *>::getEmptyKey(); + return T::getFromOpaquePointer(pointer); + } + static T getTombstoneKey() { + const void *pointer = llvm::DenseMapInfo<const void *>::getTombstoneKey(); + return T::getFromOpaquePointer(pointer); + } +}; /// Allow LLVM to steal the low bits of Attributes. template <> struct PointerLikeTypeTraits<mlir::Attribute> { Index: mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.h =================================================================== --- mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.h +++ mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.h @@ -40,25 +40,6 @@ namespace llvm { -/// spirv::Function ops hash just like pointers. -template <> -struct DenseMapInfo<mlir::spirv::FuncOp> { - static mlir::spirv::FuncOp getEmptyKey() { - auto pointer = llvm::DenseMapInfo<void *>::getEmptyKey(); - return mlir::spirv::FuncOp::getFromOpaquePointer(pointer); - } - static mlir::spirv::FuncOp getTombstoneKey() { - auto pointer = llvm::DenseMapInfo<void *>::getTombstoneKey(); - return mlir::spirv::FuncOp::getFromOpaquePointer(pointer); - } - static unsigned getHashValue(mlir::spirv::FuncOp val) { - return hash_value(val.getAsOpaquePointer()); - } - static bool isEqual(mlir::spirv::FuncOp LHS, mlir::spirv::FuncOp RHS) { - return LHS == RHS; - } -}; - /// Allow stealing the low bits of spirv::Function ops. template <> struct PointerLikeTypeTraits<mlir::spirv::FuncOp> { Index: llvm/unittests/ADT/DenseMapTest.cpp =================================================================== --- llvm/unittests/ADT/DenseMapTest.cpp +++ llvm/unittests/ADT/DenseMapTest.cpp @@ -655,4 +655,47 @@ EXPECT_EQ(Map.find(K2), Map.end()); EXPECT_EQ(Map.find(K3), Map.end()); } +} // namespace + +namespace { +struct A { + A(int value) : value(value) {} + int value; +}; +struct B : public A { + using A::A; +}; +} // namespace + +namespace llvm { +template <typename T> +struct DenseMapInfo<T, std::enable_if_t<std::is_base_of<A, T>::value>> { + static inline T getEmptyKey() { return {static_cast<int>(~0)}; } + static inline T getTombstoneKey() { return {static_cast<int>(~0U - 1)}; } + static unsigned getHashValue(const T &Val) { return Val.value; } + static bool isEqual(const T &LHS, const T &RHS) { + return LHS.value == RHS.value; + } +}; +} // namespace llvm + +namespace { +TEST(DenseMapCustomTest, SFINAEMapInfo) { + // Test that we can use a pointer to an incomplete type as a DenseMap key. + // This is an important build time optimization, since many classes have + // DenseMap members. + DenseMap<B, int> Map; + B Keys[3] = {{0}, {1}, {2}}; + Map.insert({Keys[0], 1}); + Map.insert({Keys[1], 2}); + Map.insert({Keys[2], 3}); + EXPECT_EQ(Map.count(Keys[0]), 1u); + EXPECT_EQ(Map[Keys[0]], 1); + EXPECT_EQ(Map[Keys[1]], 2); + EXPECT_EQ(Map[Keys[2]], 3); + Map.clear(); + EXPECT_EQ(Map.find(Keys[0]), Map.end()); + EXPECT_EQ(Map.find(Keys[1]), Map.end()); + EXPECT_EQ(Map.find(Keys[2]), Map.end()); } +} // namespace Index: llvm/lib/Support/APInt.cpp =================================================================== --- llvm/lib/Support/APInt.cpp +++ llvm/lib/Support/APInt.cpp @@ -569,7 +569,7 @@ hash_combine_range(Arg.U.pVal, Arg.U.pVal + Arg.getNumWords())); } -unsigned DenseMapInfo<APInt>::getHashValue(const APInt &Key) { +unsigned DenseMapInfo<APInt, void>::getHashValue(const APInt &Key) { return static_cast<unsigned>(hash_value(Key)); } Index: llvm/include/llvm/Support/TypeSize.h =================================================================== --- llvm/include/llvm/Support/TypeSize.h +++ llvm/include/llvm/Support/TypeSize.h @@ -249,7 +249,7 @@ //===----------------------------------------------------------------------===// // LinearPolySize - base class for fixed- or scalable sizes. -// ^ ^ +// ^ ^ // | | // | +----- ElementCount - Leaf class to represent an element count // | (vscale x unsigned) @@ -499,8 +499,7 @@ return OS; } -template <typename T> struct DenseMapInfo; -template <> struct DenseMapInfo<ElementCount> { +template <> struct DenseMapInfo<ElementCount, void> { static inline ElementCount getEmptyKey() { return ElementCount::getScalable(~0U); } Index: llvm/include/llvm/IR/Attributes.h =================================================================== --- llvm/include/llvm/IR/Attributes.h +++ llvm/include/llvm/IR/Attributes.h @@ -37,7 +37,6 @@ class AttributeImpl; class AttributeListImpl; class AttributeSetNode; -template<typename T> struct DenseMapInfo; class FoldingSetNodeID; class Function; class LLVMContext; @@ -266,7 +265,7 @@ /// and removing string or integer attributes involves a FoldingSet lookup. class AttributeSet { friend AttributeListImpl; - template <typename Ty> friend struct DenseMapInfo; + template <typename Ty, typename Enable> friend struct DenseMapInfo; // TODO: Extract AvailableAttrs from AttributeSetNode and store them here. // This will allow an efficient implementation of addAttribute and @@ -367,7 +366,7 @@ //===----------------------------------------------------------------------===// /// \class /// Provide DenseMapInfo for AttributeSet. -template <> struct DenseMapInfo<AttributeSet> { +template <> struct DenseMapInfo<AttributeSet, void> { static AttributeSet getEmptyKey() { auto Val = static_cast<uintptr_t>(-1); Val <<= PointerLikeTypeTraits<void *>::NumLowBitsAvailable; @@ -409,7 +408,7 @@ friend class AttributeListImpl; friend class AttributeSet; friend class AttributeSetNode; - template <typename Ty> friend struct DenseMapInfo; + template <typename Ty, typename Enable> friend struct DenseMapInfo; /// The attributes that we are managing. This can be null to represent /// the empty attributes list. @@ -899,7 +898,7 @@ //===----------------------------------------------------------------------===// /// \class /// Provide DenseMapInfo for AttributeList. -template <> struct DenseMapInfo<AttributeList> { +template <> struct DenseMapInfo<AttributeList, void> { static AttributeList getEmptyKey() { auto Val = static_cast<uintptr_t>(-1); Val <<= PointerLikeTypeTraits<void*>::NumLowBitsAvailable; Index: llvm/include/llvm/CodeGen/SelectionDAGNodes.h =================================================================== --- llvm/include/llvm/CodeGen/SelectionDAGNodes.h +++ llvm/include/llvm/CodeGen/SelectionDAGNodes.h @@ -58,7 +58,6 @@ class APInt; class Constant; -template <typename T> struct DenseMapInfo; class GlobalValue; class MachineBasicBlock; class MachineConstantPoolValue; Index: llvm/include/llvm/BinaryFormat/WasmTraits.h =================================================================== --- llvm/include/llvm/BinaryFormat/WasmTraits.h +++ llvm/include/llvm/BinaryFormat/WasmTraits.h @@ -18,10 +18,8 @@ namespace llvm { -template <typename T> struct DenseMapInfo; - // Traits for using WasmSignature in a DenseMap. -template <> struct DenseMapInfo<wasm::WasmSignature> { +template <> struct DenseMapInfo<wasm::WasmSignature, void> { static wasm::WasmSignature getEmptyKey() { wasm::WasmSignature Sig; Sig.State = wasm::WasmSignature::Empty; @@ -47,7 +45,7 @@ }; // Traits for using WasmGlobalType in a DenseMap -template <> struct DenseMapInfo<wasm::WasmGlobalType> { +template <> struct DenseMapInfo<wasm::WasmGlobalType, void> { static wasm::WasmGlobalType getEmptyKey() { return wasm::WasmGlobalType{1, true}; } @@ -64,7 +62,7 @@ }; // Traits for using WasmLimits in a DenseMap -template <> struct DenseMapInfo<wasm::WasmLimits> { +template <> struct DenseMapInfo<wasm::WasmLimits, void> { static wasm::WasmLimits getEmptyKey() { return wasm::WasmLimits{0xff, 0xff, 0xff}; } @@ -86,19 +84,19 @@ }; // Traits for using WasmTableType in a DenseMap -template <> struct DenseMapInfo<wasm::WasmTableType> { +template <> struct DenseMapInfo<wasm::WasmTableType, void> { static wasm::WasmTableType getEmptyKey() { - return wasm::WasmTableType{0, - DenseMapInfo<wasm::WasmLimits>::getEmptyKey()}; + return wasm::WasmTableType{ + 0, DenseMapInfo<wasm::WasmLimits, void>::getEmptyKey()}; } static wasm::WasmTableType getTombstoneKey() { return wasm::WasmTableType{ - 1, DenseMapInfo<wasm::WasmLimits>::getTombstoneKey()}; + 1, DenseMapInfo<wasm::WasmLimits, void>::getTombstoneKey()}; } static unsigned getHashValue(const wasm::WasmTableType &TableType) { return hash_combine( TableType.ElemType, - DenseMapInfo<wasm::WasmLimits>::getHashValue(TableType.Limits)); + DenseMapInfo<wasm::WasmLimits, void>::getHashValue(TableType.Limits)); } static bool isEqual(const wasm::WasmTableType &LHS, const wasm::WasmTableType &RHS) { Index: llvm/include/llvm/ADT/StringRef.h =================================================================== --- llvm/include/llvm/ADT/StringRef.h +++ llvm/include/llvm/ADT/StringRef.h @@ -35,7 +35,6 @@ class APInt; class hash_code; template <typename T> class SmallVectorImpl; - template <typename T> struct DenseMapInfo; class StringRef; /// Helper functions for StringRef::getAsInteger. @@ -949,7 +948,7 @@ hash_code hash_value(StringRef S); // Provide DenseMapInfo for StringRefs. - template <> struct DenseMapInfo<StringRef> { + template <> struct DenseMapInfo<StringRef, void> { static inline StringRef getEmptyKey() { return StringRef( reinterpret_cast<const char *>(~static_cast<uintptr_t>(0)), 0); Index: llvm/include/llvm/ADT/PointerIntPair.h =================================================================== --- llvm/include/llvm/ADT/PointerIntPair.h +++ llvm/include/llvm/ADT/PointerIntPair.h @@ -22,7 +22,7 @@ namespace llvm { -template <typename T> struct DenseMapInfo; +template <typename T, typename Enable> struct DenseMapInfo; template <typename PointerT, unsigned IntBits, typename PtrTraits> struct PointerIntPairInfo; @@ -192,7 +192,7 @@ // Provide specialization of DenseMapInfo for PointerIntPair. template <typename PointerTy, unsigned IntBits, typename IntType> -struct DenseMapInfo<PointerIntPair<PointerTy, IntBits, IntType>> { +struct DenseMapInfo<PointerIntPair<PointerTy, IntBits, IntType>, void> { using Ty = PointerIntPair<PointerTy, IntBits, IntType>; static Ty getEmptyKey() { Index: llvm/include/llvm/ADT/ImmutableList.h =================================================================== --- llvm/include/llvm/ADT/ImmutableList.h +++ llvm/include/llvm/ADT/ImmutableList.h @@ -220,8 +220,7 @@ // Partially-specialized Traits. //===----------------------------------------------------------------------===// -template<typename T> struct DenseMapInfo; -template<typename T> struct DenseMapInfo<ImmutableList<T>> { +template <typename T> struct DenseMapInfo<ImmutableList<T>, void> { static inline ImmutableList<T> getEmptyKey() { return reinterpret_cast<ImmutableListImpl<T>*>(-1); } Index: llvm/include/llvm/ADT/Hashing.h =================================================================== --- llvm/include/llvm/ADT/Hashing.h +++ llvm/include/llvm/ADT/Hashing.h @@ -56,7 +56,7 @@ #include <utility> namespace llvm { -template <typename T> struct DenseMapInfo; +template <typename T, typename Enable> struct DenseMapInfo; /// An opaque object representing a hash code. /// @@ -678,7 +678,7 @@ return hash_combine_range(arg.begin(), arg.end()); } -template <> struct DenseMapInfo<hash_code> { +template <> struct DenseMapInfo<hash_code, void> { static inline hash_code getEmptyKey() { return hash_code(-1); } static inline hash_code getTombstoneKey() { return hash_code(-2); } static unsigned getHashValue(hash_code val) { return val; } Index: llvm/include/llvm/ADT/DenseMapInfo.h =================================================================== --- llvm/include/llvm/ADT/DenseMapInfo.h +++ llvm/include/llvm/ADT/DenseMapInfo.h @@ -39,7 +39,12 @@ } // end namespace detail -template<typename T> +/// An information struct used to provide DenseMap with the various necessary +/// components for a given value type `T`. `Enable` is an optional additional +/// parameter that is used to support SFINAE (generally using std::enable_if_t) +/// in derived DenseMapInfo specializations; in non-SFINAE use cases this should +/// just be `void`. +template<typename T, typename Enable = void> struct DenseMapInfo { //static inline T getEmptyKey(); //static inline T getTombstoneKey(); Index: llvm/include/llvm/ADT/ArrayRef.h =================================================================== --- llvm/include/llvm/ADT/ArrayRef.h +++ llvm/include/llvm/ADT/ArrayRef.h @@ -26,8 +26,6 @@ namespace llvm { - template<typename T> struct DenseMapInfo; - /// ArrayRef - Represent a constant reference to an array (0 or more elements /// consecutively in memory), i.e. a start pointer and a length. It allows /// various APIs to take consecutive elements easily and conveniently. @@ -572,7 +570,7 @@ } // Provide DenseMapInfo for ArrayRefs. - template <typename T> struct DenseMapInfo<ArrayRef<T>> { + template <typename T> struct DenseMapInfo<ArrayRef<T>, void> { static inline ArrayRef<T> getEmptyKey() { return ArrayRef<T>( reinterpret_cast<const T *>(~static_cast<uintptr_t>(0)), size_t(0)); Index: llvm/include/llvm/ADT/APSInt.h =================================================================== --- llvm/include/llvm/ADT/APSInt.h +++ llvm/include/llvm/ADT/APSInt.h @@ -344,17 +344,17 @@ } /// Provide DenseMapInfo for APSInt, using the DenseMapInfo for APInt. -template <> struct DenseMapInfo<APSInt> { +template <> struct DenseMapInfo<APSInt, void> { static inline APSInt getEmptyKey() { - return APSInt(DenseMapInfo<APInt>::getEmptyKey()); + return APSInt(DenseMapInfo<APInt, void>::getEmptyKey()); } static inline APSInt getTombstoneKey() { - return APSInt(DenseMapInfo<APInt>::getTombstoneKey()); + return APSInt(DenseMapInfo<APInt, void>::getTombstoneKey()); } static unsigned getHashValue(const APSInt &Key) { - return DenseMapInfo<APInt>::getHashValue(Key); + return DenseMapInfo<APInt, void>::getHashValue(Key); } static bool isEqual(const APSInt &LHS, const APSInt &RHS) { Index: llvm/include/llvm/ADT/APInt.h =================================================================== --- llvm/include/llvm/ADT/APInt.h +++ llvm/include/llvm/ADT/APInt.h @@ -31,7 +31,7 @@ template <typename T> class SmallVectorImpl; template <typename T> class ArrayRef; template <typename T> class Optional; -template <typename T> struct DenseMapInfo; +template <typename T, typename Enable> struct DenseMapInfo; class APInt; @@ -1817,7 +1817,7 @@ unsigned BitWidth; ///< The number of bits in this APInt. - friend struct DenseMapInfo<APInt>; + friend struct DenseMapInfo<APInt, void>; friend class APSInt; /// This constructor is used only internally for speed of construction of @@ -2251,7 +2251,7 @@ void LoadIntFromMemory(APInt &IntVal, const uint8_t *Src, unsigned LoadBytes); /// Provide DenseMapInfo for APInt. -template <> struct DenseMapInfo<APInt> { +template <> struct DenseMapInfo<APInt, void> { static inline APInt getEmptyKey() { APInt V(nullptr, 0); V.U.VAL = 0; Index: lldb/include/lldb/Utility/ConstString.h =================================================================== --- lldb/include/lldb/Utility/ConstString.h +++ lldb/include/lldb/Utility/ConstString.h @@ -409,7 +409,7 @@ static size_t StaticMemorySize(); protected: - template <typename T> friend struct ::llvm::DenseMapInfo; + template <typename T, typename Enable> friend struct ::llvm::DenseMapInfo; /// Only used by DenseMapInfo. static ConstString FromStringPoolPointer(const char *ptr) { ConstString s; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -74,7 +74,6 @@ namespace llvm { class APSInt; - template <typename ValueT> struct DenseMapInfo; template <typename ValueT, typename ValueInfoT> class DenseSet; class SmallBitVector; struct InlineAsmIdentifierInfo; Index: clang/include/clang/Basic/SourceLocation.h =================================================================== --- clang/include/clang/Basic/SourceLocation.h +++ clang/include/clang/Basic/SourceLocation.h @@ -23,8 +23,6 @@ namespace llvm { -template <typename T> struct DenseMapInfo; - class FoldingSetNodeID; template <typename T> struct FoldingSetTrait; @@ -467,7 +465,7 @@ /// Define DenseMapInfo so that FileID's can be used as keys in DenseMap and /// DenseSets. template <> - struct DenseMapInfo<clang::FileID> { + struct DenseMapInfo<clang::FileID, void> { static clang::FileID getEmptyKey() { return {}; } @@ -488,7 +486,7 @@ /// Define DenseMapInfo so that SourceLocation's can be used as keys in /// DenseMap and DenseSet. This trait class is eqivalent to /// DenseMapInfo<unsigned> which uses SourceLocation::ID is used as a key. - template <> struct DenseMapInfo<clang::SourceLocation> { + template <> struct DenseMapInfo<clang::SourceLocation, void> { static clang::SourceLocation getEmptyKey() { constexpr clang::SourceLocation::UIntTy Zero = 0; return clang::SourceLocation::getFromRawEncoding(~Zero); Index: clang/include/clang/AST/TypeOrdering.h =================================================================== --- clang/include/clang/AST/TypeOrdering.h +++ clang/include/clang/AST/TypeOrdering.h @@ -34,7 +34,6 @@ } namespace llvm { - template<class> struct DenseMapInfo; template<> struct DenseMapInfo<clang::QualType> { static inline clang::QualType getEmptyKey() { return clang::QualType(); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits