[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