[llvm] 9835f8a - [ADT] Reland: Remove CRTP from FoldingSet and ContextualFoldingSet (NFC) (#217058)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 18 19:57:03 PDT 2026
Author: Kazu Hirata
Date: 2026-08-18T19:56:58-07:00
New Revision: 9835f8acf3baf3b489500b3bfa42fc1b0c9d48d0
URL: https://github.com/llvm/llvm-project/commit/9835f8acf3baf3b489500b3bfa42fc1b0c9d48d0
DIFF: https://github.com/llvm/llvm-project/commit/9835f8acf3baf3b489500b3bfa42fc1b0c9d48d0.diff
LOG: [ADT] Reland: Remove CRTP from FoldingSet and ContextualFoldingSet (NFC) (#217058)
This patch relands #216830 with a fix for MSVC build failures.
In the original patch, FoldingSetInfo was defined as a static constexpr
member variable of FoldingSetImpl. On MSVC, instantiating
FoldingSetImpl<T> (e.g. in LLVMContextImpl.h) eagerly evaluates the
static constexpr member variable and its lambdas containing
static_cast<T *>(N). When T is an incomplete type (such as AttributeImpl
forward-declared in LLVMContextImpl.h and compiled in Metadata.cpp),
this
caused MSVC to fail with C2440 because static_cast requires a complete
type.
This patch wraps FoldingSetInfo in a static getFoldingSetInfo() member
function so that instantiation is deferred until the function is
actually
called, such as during InsertNode or FindNodeOrInsertPos.
Assisted-by: Antigravity
Added:
Modified:
llvm/include/llvm/ADT/FoldingSet.h
Removed:
################################################################################
diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index 4425158991ce5..5e8bb9a87e64e 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -114,6 +114,8 @@ class StringRef;
/// This class provides default implementations for FoldingSetTrait
/// implementations.
template <typename T> struct DefaultFoldingSetTrait {
+ struct ContextStorage {};
+
static void Profile(const T &X, FoldingSetNodeID &ID) { X.Profile(ID); }
static void Profile(T &X, FoldingSetNodeID &ID) { X.Profile(ID); }
@@ -143,6 +145,12 @@ struct FoldingSetTrait : public DefaultFoldingSetTrait<T> {};
/// Like DefaultFoldingSetTrait, but for ContextualFoldingSets.
template <typename T, typename Ctx> struct DefaultContextualFoldingSetTrait {
+ struct ContextStorage {
+ Ctx Context;
+ explicit ContextStorage(Ctx Context) : Context(Context) {}
+ Ctx getContext() const { return Context; }
+ };
+
static void Profile(T &X, FoldingSetNodeID &ID, Ctx Context) {
X.Profile(ID, Context);
}
@@ -434,11 +442,57 @@ inline unsigned DefaultContextualFoldingSetTrait<T, Ctx>::ComputeHash(
//===----------------------------------------------------------------------===//
/// An implementation detail that lets us share code between FoldingSet and
/// ContextualFoldingSet.
-template <class Derived, class T> class FoldingSetImpl : public FoldingSetBase {
-protected:
- explicit FoldingSetImpl(unsigned Log2InitSize)
+template <class T, class Trait = FoldingSetTrait<T>>
+class FoldingSetImpl : public FoldingSetBase, public Trait::ContextStorage {
+ // We define Info inside a static member function rather than as a static
+ // constexpr member variable to avoid eager instantiation on MSVC when T is an
+ // incomplete type.
+ static const FoldingSetBase::FoldingSetInfo &getFoldingSetInfo() {
+ static constexpr FoldingSetBase::FoldingSetInfo Info = {
+ // GetNodeProfile
+ [](const FoldingSetBase *Base, FoldingSetNode *N,
+ FoldingSetNodeID &ID) {
+ if constexpr (std::is_empty_v<typename Trait::ContextStorage>)
+ Trait::Profile(*static_cast<T *>(N), ID);
+ else
+ Trait::Profile(
+ *static_cast<T *>(N), ID,
+ static_cast<const FoldingSetImpl *>(Base)->getContext());
+ },
+ // NodeEquals
+ [](const FoldingSetBase *Base, FoldingSetNode *N,
+ const FoldingSetNodeID &ID, unsigned IDHash,
+ FoldingSetNodeID &TempID) {
+ if constexpr (std::is_empty_v<typename Trait::ContextStorage>)
+ return Trait::Equals(*static_cast<T *>(N), ID, IDHash, TempID);
+ else
+ return Trait::Equals(
+ *static_cast<T *>(N), ID, IDHash, TempID,
+ static_cast<const FoldingSetImpl *>(Base)->getContext());
+ },
+ // ComputeNodeHash
+ [](const FoldingSetBase *Base, FoldingSetNode *N,
+ FoldingSetNodeID &TempID) {
+ if constexpr (std::is_empty_v<typename Trait::ContextStorage>)
+ return Trait::ComputeHash(*static_cast<T *>(N), TempID);
+ else
+ return Trait::ComputeHash(
+ *static_cast<T *>(N), TempID,
+ static_cast<const FoldingSetImpl *>(Base)->getContext());
+ }};
+ return Info;
+ }
+
+public:
+ explicit FoldingSetImpl(unsigned Log2InitSize = 6)
: FoldingSetBase(Log2InitSize) {}
+ template <typename C, typename = std::enable_if_t<std::is_constructible_v<
+ typename Trait::ContextStorage, C>>>
+ explicit FoldingSetImpl(C &&Context, unsigned Log2InitSize = 6)
+ : FoldingSetBase(Log2InitSize),
+ Trait::ContextStorage(std::forward<C>(Context)) {}
+
FoldingSetImpl(FoldingSetImpl &&Arg) = default;
FoldingSetImpl &operator=(FoldingSetImpl &&RHS) = default;
~FoldingSetImpl() = default;
@@ -458,7 +512,7 @@ template <class Derived, class T> class FoldingSetImpl : public FoldingSetBase {
/// won't cause a rebucket operation. reserve is permitted to allocate more
/// space than requested by EltCount.
void reserve(unsigned EltCount) {
- FoldingSetBase::reserve(EltCount, Derived::getFoldingSetInfo());
+ FoldingSetBase::reserve(EltCount, getFoldingSetInfo());
}
/// Remove a node from the folding set, returning true if one
@@ -469,21 +523,21 @@ template <class Derived, class T> class FoldingSetImpl : public FoldingSetBase {
/// return it. Otherwise, insert 'N' and return it instead.
T *GetOrInsertNode(T *N) {
return static_cast<T *>(
- FoldingSetBase::GetOrInsertNode(N, Derived::getFoldingSetInfo()));
+ FoldingSetBase::GetOrInsertNode(N, getFoldingSetInfo()));
}
/// Look up the node specified by ID. If it exists, return it. If not,
/// return the insertion token that will make insertion faster.
T *FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos) {
return static_cast<T *>(FoldingSetBase::FindNodeOrInsertPos(
- ID, InsertPos, Derived::getFoldingSetInfo()));
+ ID, InsertPos, getFoldingSetInfo()));
}
/// Insert the specified node into the folding set, knowing that
/// it is not already in the folding set. InsertPos must be obtained from
/// FindNodeOrInsertPos.
void InsertNode(T *N, void *InsertPos) {
- FoldingSetBase::InsertNode(N, InsertPos, Derived::getFoldingSetInfo());
+ FoldingSetBase::InsertNode(N, InsertPos, getFoldingSetInfo());
}
/// Insert the specified node into the folding set, knowing that it is not
@@ -504,47 +558,8 @@ template <class Derived, class T> class FoldingSetImpl : public FoldingSetBase {
/// moved-from state is not a valid state for anything other than
/// move-assigning and destroying. This is primarily to enable movable APIs
/// that incorporate these objects.
-template <class T> class FoldingSet : public FoldingSetImpl<FoldingSet<T>, T> {
- using Super = FoldingSetImpl<FoldingSet, T>;
- using Node = typename Super::Node;
-
- /// Each instantiation of the FoldingSet needs to provide a
- /// way to convert nodes into a unique specifier.
- static void GetNodeProfile(const FoldingSetBase *, Node *N,
- FoldingSetNodeID &ID) {
- T *TN = static_cast<T *>(N);
- FoldingSetTrait<T>::Profile(*TN, ID);
- }
-
- /// Instantiations may optionally provide a way to compare a
- /// node with a specified ID.
- static bool NodeEquals(const FoldingSetBase *, Node *N,
- const FoldingSetNodeID &ID, unsigned IDHash,
- FoldingSetNodeID &TempID) {
- T *TN = static_cast<T *>(N);
- return FoldingSetTrait<T>::Equals(*TN, ID, IDHash, TempID);
- }
-
- /// Instantiations may optionally provide a way to compute a
- /// hash value directly from a node.
- static unsigned ComputeNodeHash(const FoldingSetBase *, Node *N,
- FoldingSetNodeID &TempID) {
- T *TN = static_cast<T *>(N);
- return FoldingSetTrait<T>::ComputeHash(*TN, TempID);
- }
-
- static const FoldingSetBase::FoldingSetInfo &getFoldingSetInfo() {
- static constexpr FoldingSetBase::FoldingSetInfo Info = {
- GetNodeProfile, NodeEquals, ComputeNodeHash};
- return Info;
- }
- friend Super;
-
-public:
- explicit FoldingSet(unsigned Log2InitSize = 6) : Super(Log2InitSize) {}
- FoldingSet(FoldingSet &&Arg) = default;
- FoldingSet &operator=(FoldingSet &&RHS) = default;
-};
+template <class T, class Trait = FoldingSetTrait<T>>
+using FoldingSet = FoldingSetImpl<T, Trait>;
//===----------------------------------------------------------------------===//
/// This template class is a further refinement of FoldingSet which provides a
@@ -555,58 +570,8 @@ template <class T> class FoldingSet : public FoldingSetImpl<FoldingSet<T>, T> {
/// function with signature
/// void Profile(FoldingSetNodeID &, Ctx);
template <class T, class Ctx>
-class ContextualFoldingSet
- : public FoldingSetImpl<ContextualFoldingSet<T, Ctx>, T> {
- // Unfortunately, this can't derive from FoldingSet<T> because the
- // construction of the vtable for FoldingSet<T> requires
- // FoldingSet<T>::GetNodeProfile to be instantiated, which in turn
- // requires a single-argument T::Profile().
-
- using Super = FoldingSetImpl<ContextualFoldingSet, T>;
- using Node = typename Super::Node;
-
- Ctx Context;
-
- static const Ctx &getContext(const FoldingSetBase *Base) {
- return static_cast<const ContextualFoldingSet *>(Base)->Context;
- }
-
- /// Each instantiatation of the FoldingSet needs to provide a way to convert
- /// nodes into a unique specifier.
- static void GetNodeProfile(const FoldingSetBase *Base, Node *N,
- FoldingSetNodeID &ID) {
- T *TN = static_cast<T *>(N);
- ContextualFoldingSetTrait<T, Ctx>::Profile(*TN, ID, getContext(Base));
- }
-
- static bool NodeEquals(const FoldingSetBase *Base, Node *N,
- const FoldingSetNodeID &ID, unsigned IDHash,
- FoldingSetNodeID &TempID) {
- T *TN = static_cast<T *>(N);
- return ContextualFoldingSetTrait<T, Ctx>::Equals(*TN, ID, IDHash, TempID,
- getContext(Base));
- }
-
- static unsigned ComputeNodeHash(const FoldingSetBase *Base, Node *N,
- FoldingSetNodeID &TempID) {
- T *TN = static_cast<T *>(N);
- return ContextualFoldingSetTrait<T, Ctx>::ComputeHash(*TN, TempID,
- getContext(Base));
- }
-
- static const FoldingSetBase::FoldingSetInfo &getFoldingSetInfo() {
- static constexpr FoldingSetBase::FoldingSetInfo Info = {
- GetNodeProfile, NodeEquals, ComputeNodeHash};
- return Info;
- }
- friend Super;
-
-public:
- explicit ContextualFoldingSet(Ctx Context, unsigned Log2InitSize = 6)
- : Super(Log2InitSize), Context(Context) {}
-
- Ctx getContext() const { return Context; }
-};
+using ContextualFoldingSet =
+ FoldingSetImpl<T, ContextualFoldingSetTrait<T, Ctx>>;
//===----------------------------------------------------------------------===//
/// This template class combines a FoldingSet and a vector to provide the
More information about the llvm-commits
mailing list