[llvm] [ADT] Add DebugEpochBase::HandleBase::isComparableWith (PR #219738)

Kazu Hirata via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 29 19:14:07 PDT 2026


https://github.com/kazutakahirata updated https://github.com/llvm/llvm-project/pull/219738

>From f7a9c65d385397b218c6b08aa3a789e4867e68ea Mon Sep 17 00:00:00 2001
From: Kazu Hirata <kazu at google.com>
Date: Wed, 26 Aug 2026 21:00:29 -0700
Subject: [PATCH 1/2] [ADT] Add DebugEpochBase::HandleBase::isComparableWith
 (NFC)

This patch unifies operator== across hash table iterators with
DebugEpochBase::HandleBase::isComparableWith, performing the following
safety checks:

- LHS is either default-constructed or in sync with its container.

- RHS is likewise either default-constructed or in sync with its container.

- Both iterators belong to the same container instance and share the
  same state (without intervening mutations).

Assisted-by: Antigravity
---
 llvm/include/llvm/ADT/DenseMap.h     |  7 +------
 llvm/include/llvm/ADT/EpochTracker.h | 10 ++++++++++
 llvm/include/llvm/ADT/FoldingSet.h   |  2 +-
 llvm/include/llvm/ADT/SmallPtrSet.h  |  3 ++-
 4 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
index 1480031c8d7da..6d036d4a0729f 100644
--- a/llvm/include/llvm/ADT/DenseMap.h
+++ b/llvm/include/llvm/ADT/DenseMap.h
@@ -1338,12 +1338,7 @@ class DenseMapIterator : DebugEpochBase::HandleBase {
 
   [[nodiscard]] friend bool operator==(const DenseMapIterator &LHS,
                                        const DenseMapIterator &RHS) {
-    assert((!LHS.getEpochAddress() || LHS.isHandleInSync()) &&
-           "handle not in sync!");
-    assert((!RHS.getEpochAddress() || RHS.isHandleInSync()) &&
-           "handle not in sync!");
-    assert(LHS.getEpochAddress() == RHS.getEpochAddress() &&
-           "comparing incomparable iterators!");
+    assert(LHS.isComparableWith(RHS) && "incomparable iterators!");
     return LHS.Ptr == RHS.Ptr;
   }
 
diff --git a/llvm/include/llvm/ADT/EpochTracker.h b/llvm/include/llvm/ADT/EpochTracker.h
index fc41d6f2c92d2..93c0322b70616 100644
--- a/llvm/include/llvm/ADT/EpochTracker.h
+++ b/llvm/include/llvm/ADT/EpochTracker.h
@@ -71,6 +71,15 @@ class DebugEpochBase {
     /// HandleBase instance.
     bool isHandleInSync() const { return *EpochAddress == EpochAtCreation; }
 
+    /// Returns true if this handle and RHS can be compared. Both must be either
+    /// default-constructed or point into the same container and no mutation
+    /// has occurred since they were created.
+    bool isComparableWith(const HandleBase &RHS) const {
+      return (!EpochAddress || isHandleInSync()) &&
+             (!RHS.EpochAddress || RHS.isHandleInSync()) &&
+             EpochAddress == RHS.EpochAddress;
+    }
+
     /// Returns a pointer to the epoch word stored in the data structure
     /// this handle points into.  Can be used to check if two iterators point
     /// into the same data structure.
@@ -94,6 +103,7 @@ class DebugEpochBase {
     HandleBase() = default;
     explicit HandleBase(const DebugEpochBase *) {}
     bool isHandleInSync() const { return true; }
+    bool isComparableWith(const HandleBase &) const { return true; }
     const void *getEpochAddress() const { return nullptr; }
   };
 };
diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index c984c00b86fd1..9499aa784b7a0 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -635,7 +635,7 @@ template <class T> class FoldingSetIterator : DebugEpochBase::HandleBase {
   }
 
   bool operator==(const FoldingSetIterator &RHS) const {
-    assert(isHandleInSync() && RHS.isHandleInSync() && "handle not in sync!");
+    assert(isComparableWith(RHS) && "incomparable iterators!");
     return Bucket == RHS.Bucket;
   }
   bool operator!=(const FoldingSetIterator &RHS) const {
diff --git a/llvm/include/llvm/ADT/SmallPtrSet.h b/llvm/include/llvm/ADT/SmallPtrSet.h
index 6803c524ef068..1a556d6a4145a 100644
--- a/llvm/include/llvm/ADT/SmallPtrSet.h
+++ b/llvm/include/llvm/ADT/SmallPtrSet.h
@@ -336,10 +336,11 @@ class LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE SmallPtrSetIterator
   }
 
   bool operator==(const SmallPtrSetIterator &RHS) const {
+    assert(isComparableWith(RHS) && "incomparable iterators!");
     return Bucket == RHS.Bucket;
   }
   bool operator!=(const SmallPtrSetIterator &RHS) const {
-    return Bucket != RHS.Bucket;
+    return !(*this == RHS);
   }
 };
 

>From e21fae4b392c663e3461bfc7073a72c5c9a69e18 Mon Sep 17 00:00:00 2001
From: Kazu Hirata <kazu at google.com>
Date: Sat, 29 Aug 2026 19:04:57 -0700
Subject: [PATCH 2/2] Disable the SmallPtrSet change due to lldb failures.  Add
 unit tests.

---
 llvm/include/llvm/ADT/SmallPtrSet.h |  3 +--
 llvm/unittests/ADT/DenseMapTest.cpp | 18 ++++++++++++++++++
 llvm/unittests/ADT/FoldingSet.cpp   | 19 +++++++++++++++++++
 3 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/ADT/SmallPtrSet.h b/llvm/include/llvm/ADT/SmallPtrSet.h
index 1a556d6a4145a..6803c524ef068 100644
--- a/llvm/include/llvm/ADT/SmallPtrSet.h
+++ b/llvm/include/llvm/ADT/SmallPtrSet.h
@@ -336,11 +336,10 @@ class LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE SmallPtrSetIterator
   }
 
   bool operator==(const SmallPtrSetIterator &RHS) const {
-    assert(isComparableWith(RHS) && "incomparable iterators!");
     return Bucket == RHS.Bucket;
   }
   bool operator!=(const SmallPtrSetIterator &RHS) const {
-    return !(*this == RHS);
+    return Bucket != RHS.Bucket;
   }
 };
 
diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index 2907a1fefdb03..3932ed8c13b78 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -1158,6 +1158,24 @@ TEST(DenseMapCustomTest, EraseInvalidatesIterators) {
   M.erase(M.find(2));
   EXPECT_DEATH((void)It->second, "invalid iterator access");
 }
+
+TEST(DenseMapCustomTest, IteratorComparability) {
+  DenseMap<int, int>::iterator I1, I2;
+  EXPECT_EQ(I1, I2);
+
+  DenseMap<int, int> Map1, Map2;
+  Map1.try_emplace(1, 10);
+  Map2.try_emplace(2, 20);
+  EXPECT_DEATH((void)(Map1.begin() == Map2.begin()), "incomparable iterators");
+}
+
+TEST(DenseMapCustomTest, InsertInvalidatesIteratorComparison) {
+  DenseMap<int, int> Map;
+  Map.try_emplace(1, 10);
+  auto It = Map.begin();
+  Map.try_emplace(2, 20);
+  EXPECT_DEATH((void)(It == Map.end()), "incomparable iterators");
+}
 #endif
 
 } // namespace
diff --git a/llvm/unittests/ADT/FoldingSet.cpp b/llvm/unittests/ADT/FoldingSet.cpp
index 2556d64bae7a3..1b694e84780d3 100644
--- a/llvm/unittests/ADT/FoldingSet.cpp
+++ b/llvm/unittests/ADT/FoldingSet.cpp
@@ -489,6 +489,25 @@ TEST(FoldingSetTest, MoveInvalidatesIterators) {
   FoldingSet<TrivialPair> Other(std::move(Set));
   EXPECT_DEATH((void)It->Value, "invalid iterator access");
 }
+
+TEST(FoldingSetTest, IteratorComparability) {
+  FoldingSet<TrivialPair> Set1, Set2;
+  TrivialPair T1(1, 1), T2(2, 2);
+  Set1.InsertNode(&T1);
+  Set2.InsertNode(&T2);
+  EXPECT_TRUE(Set1.begin() == Set1.begin());
+  EXPECT_FALSE(Set1.begin() == Set1.end());
+  EXPECT_DEATH((void)(Set1.begin() == Set2.begin()), "incomparable iterators");
+}
+
+TEST(FoldingSetTest, InsertInvalidatesIteratorComparison) {
+  FoldingSet<TrivialPair> Set;
+  TrivialPair T1(1, 1), T2(2, 2);
+  Set.InsertNode(&T1);
+  auto It = Set.begin();
+  Set.InsertNode(&T2);
+  EXPECT_DEATH((void)(It == Set.end()), "incomparable iterators");
+}
 #endif
 
 // The InsertPos token is a hash, not a position, so a rehash cannot stale it.



More information about the llvm-commits mailing list