[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