Author: Kazu Hirata
Date: 2025-08-31T19:18:21-07:00
New Revision: a428b3081a1aa73e3fddb70127ba3e0fb85d6e3d

URL: 
https://github.com/llvm/llvm-project/commit/a428b3081a1aa73e3fddb70127ba3e0fb85d6e3d
DIFF: 
https://github.com/llvm/llvm-project/commit/a428b3081a1aa73e3fddb70127ba3e0fb85d6e3d.diff

LOG: [ADT] Refactor StringMap iterators (NFC) (#156137)

StringMap has four iterator classes:

- StringMapIterBase
- StringMapIterator
- StringMapConstIterator
- StringMapKeyIterator

This patch consolidates the first three into one class, namely
StringMapIterBase, adds a boolean template parameter to indicate
desired constness, and then use "using" directives to specialize the
common class:

  using const_iterator = StringMapIterBase<ValueTy, true>;
  using iterator = StringMapIterBase<ValueTy, false>;

just like how we simplified DenseMapIterator.

Remarks:

- This patch drops CRTP and iterator_facade_base for simplicity.  For
  fairly simple forward iterators, iterator_facade_base doesn't buy us
  much.  We just have to write a few "using" directives and operator!=
  manually.

- StringMapIterBase has a SFINAE-based constructor to construct a
  const iterator from a non-const one just like DenseMapIterator.

- We now rely on compiler-generated copy and assignment operators.

Added: 
    

Modified: 
    clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
    clang/lib/Driver/OffloadBundler.cpp
    llvm/include/llvm/ADT/StringMap.h
    llvm/unittests/ADT/StringMapTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp 
b/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
index 36b6007b58a51..4382f9df5336e 100644
--- a/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
+++ b/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
@@ -39,7 +39,7 @@ bool MultipleInheritanceCheck::getInterfaceStatus(const 
CXXRecordDecl *Node,
                                                   bool &IsInterface) const {
   assert(Node->getIdentifier());
   StringRef Name = Node->getIdentifier()->getName();
-  llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name);
+  auto Pair = InterfaceMap.find(Name);
   if (Pair == InterfaceMap.end())
     return false;
   IsInterface = Pair->second;

diff  --git a/clang/lib/Driver/OffloadBundler.cpp 
b/clang/lib/Driver/OffloadBundler.cpp
index e83aee01b75cb..f69ac41dddb3e 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -1936,8 +1936,7 @@ Error OffloadBundler::UnbundleArchive() {
   /// Write out an archive for each target
   for (auto &Target : BundlerConfig.TargetNames) {
     StringRef FileName = TargetOutputFileNameMap[Target];
-    StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers =
-        OutputArchivesMap.find(Target);
+    auto CurArchiveMembers = OutputArchivesMap.find(Target);
     if (CurArchiveMembers != OutputArchivesMap.end()) {
       if (Error WriteErr = writeArchive(FileName, 
CurArchiveMembers->getValue(),
                                         SymtabWritingMode::NormalSymtab,

diff  --git a/llvm/include/llvm/ADT/StringMap.h 
b/llvm/include/llvm/ADT/StringMap.h
index e8100abec8d3a..ac66444968f8e 100644
--- a/llvm/include/llvm/ADT/StringMap.h
+++ b/llvm/include/llvm/ADT/StringMap.h
@@ -21,11 +21,11 @@
 #include "llvm/Support/PointerLikeTypeTraits.h"
 #include <initializer_list>
 #include <iterator>
+#include <type_traits>
 
 namespace llvm {
 
-template <typename ValueTy> class StringMapConstIterator;
-template <typename ValueTy> class StringMapIterator;
+template <typename ValueTy, bool IsConst> class StringMapIterBase;
 template <typename ValueTy> class StringMapKeyIterator;
 
 /// StringMapImpl - This is the base class of StringMap that is shared among
@@ -217,8 +217,8 @@ class LLVM_ALLOCATORHOLDER_EMPTYBASE StringMap
   using value_type = StringMapEntry<ValueTy>;
   using size_type = size_t;
 
-  using const_iterator = StringMapConstIterator<ValueTy>;
-  using iterator = StringMapIterator<ValueTy>;
+  using const_iterator = StringMapIterBase<ValueTy, true>;
+  using iterator = StringMapIterBase<ValueTy, false>;
 
   iterator begin() { return iterator(TheTable, NumBuckets == 0); }
   iterator end() { return iterator(TheTable + NumBuckets, true); }
@@ -431,14 +431,17 @@ class LLVM_ALLOCATORHOLDER_EMPTYBASE StringMap
   }
 };
 
-template <typename DerivedTy, typename ValueTy>
-class StringMapIterBase
-    : public iterator_facade_base<DerivedTy, std::forward_iterator_tag,
-                                  ValueTy> {
-protected:
+template <typename ValueTy, bool IsConst> class StringMapIterBase {
   StringMapEntryBase **Ptr = nullptr;
 
 public:
+  using iterator_category = std::forward_iterator_tag;
+  using value_type = StringMapEntry<ValueTy>;
+  using 
diff erence_type = std::ptr
diff _t;
+  using pointer = std::conditional_t<IsConst, const value_type *, value_type 
*>;
+  using reference =
+      std::conditional_t<IsConst, const value_type &, value_type &>;
+
   StringMapIterBase() = default;
 
   explicit StringMapIterBase(StringMapEntryBase **Bucket,
@@ -448,85 +451,56 @@ class StringMapIterBase
       AdvancePastEmptyBuckets();
   }
 
-  DerivedTy &operator=(const DerivedTy &Other) {
-    Ptr = Other.Ptr;
-    return static_cast<DerivedTy &>(*this);
-  }
-
-  friend bool operator==(const DerivedTy &LHS, const DerivedTy &RHS) {
-    return LHS.Ptr == RHS.Ptr;
-  }
+  reference operator*() const { return *static_cast<value_type *>(*Ptr); }
+  pointer operator->() const { return static_cast<value_type *>(*Ptr); }
 
-  DerivedTy &operator++() { // Preincrement
+  StringMapIterBase &operator++() { // Preincrement
     ++Ptr;
     AdvancePastEmptyBuckets();
-    return static_cast<DerivedTy &>(*this);
+    return *this;
   }
 
-  DerivedTy operator++(int) { // Post-increment
-    DerivedTy Tmp(Ptr);
+  StringMapIterBase operator++(int) { // Post-increment
+    StringMapIterBase Tmp(*this);
     ++*this;
     return Tmp;
   }
 
-private:
-  void AdvancePastEmptyBuckets() {
-    while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
-      ++Ptr;
+  template <bool ToConst,
+            typename = typename std::enable_if<!IsConst && ToConst>::type>
+  operator StringMapIterBase<ValueTy, ToConst>() const {
+    return StringMapIterBase<ValueTy, ToConst>(Ptr, true);
   }
-};
-
-template <typename ValueTy>
-class StringMapConstIterator
-    : public StringMapIterBase<StringMapConstIterator<ValueTy>,
-                               const StringMapEntry<ValueTy>> {
-  using base = StringMapIterBase<StringMapConstIterator<ValueTy>,
-                                 const StringMapEntry<ValueTy>>;
-
-public:
-  StringMapConstIterator() = default;
-  explicit StringMapConstIterator(StringMapEntryBase **Bucket,
-                                  bool NoAdvance = false)
-      : base(Bucket, NoAdvance) {}
 
-  const StringMapEntry<ValueTy> &operator*() const {
-    return *static_cast<const StringMapEntry<ValueTy> *>(*this->Ptr);
+  friend bool operator==(const StringMapIterBase &LHS,
+                         const StringMapIterBase &RHS) {
+    return LHS.Ptr == RHS.Ptr;
   }
-};
-
-template <typename ValueTy>
-class StringMapIterator : public StringMapIterBase<StringMapIterator<ValueTy>,
-                                                   StringMapEntry<ValueTy>> {
-  using base =
-      StringMapIterBase<StringMapIterator<ValueTy>, StringMapEntry<ValueTy>>;
 
-public:
-  StringMapIterator() = default;
-  explicit StringMapIterator(StringMapEntryBase **Bucket,
-                             bool NoAdvance = false)
-      : base(Bucket, NoAdvance) {}
-
-  StringMapEntry<ValueTy> &operator*() const {
-    return *static_cast<StringMapEntry<ValueTy> *>(*this->Ptr);
+  friend bool operator!=(const StringMapIterBase &LHS,
+                         const StringMapIterBase &RHS) {
+    return !(LHS == RHS);
   }
 
-  operator StringMapConstIterator<ValueTy>() const {
-    return StringMapConstIterator<ValueTy>(this->Ptr, true);
+private:
+  void AdvancePastEmptyBuckets() {
+    while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal())
+      ++Ptr;
   }
 };
 
 template <typename ValueTy>
 class StringMapKeyIterator
     : public iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
-                                   StringMapConstIterator<ValueTy>,
+                                   StringMapIterBase<ValueTy, true>,
                                    std::forward_iterator_tag, StringRef> {
   using base = iterator_adaptor_base<StringMapKeyIterator<ValueTy>,
-                                     StringMapConstIterator<ValueTy>,
+                                     StringMapIterBase<ValueTy, true>,
                                      std::forward_iterator_tag, StringRef>;
 
 public:
   StringMapKeyIterator() = default;
-  explicit StringMapKeyIterator(StringMapConstIterator<ValueTy> Iter)
+  explicit StringMapKeyIterator(StringMapIterBase<ValueTy, true> Iter)
       : base(std::move(Iter)) {}
 
   StringRef operator*() const { return this->wrapped()->getKey(); }

diff  --git a/llvm/unittests/ADT/StringMapTest.cpp 
b/llvm/unittests/ADT/StringMapTest.cpp
index 35135cdb297e7..92ae364d45d5e 100644
--- a/llvm/unittests/ADT/StringMapTest.cpp
+++ b/llvm/unittests/ADT/StringMapTest.cpp
@@ -693,4 +693,58 @@ TEST(StringMapCustomTest, StringMapEntrySize) {
   EXPECT_EQ(LargeValue, Key.size());
 }
 
+TEST(StringMapCustomTest, NonConstIterator) {
+  StringMap<int> Map;
+  Map["key"] = 1;
+
+  // Check that Map.begin() returns a non-const iterator.
+  static_assert(
+      std::is_same_v<decltype(Map.begin()), StringMap<int>::iterator>);
+
+  // Check that the value_type of a non-const iterator is not a const type.
+  static_assert(
+      !std::is_const_v<StringMap<int>::iterator::value_type>,
+      "The value_type of a non-const iterator should not be a const type.");
+
+  // Check that pointer and reference types are not const.
+  static_assert(std::is_same_v<StringMap<int>::iterator::pointer,
+                               StringMap<int>::iterator::value_type *>);
+  static_assert(std::is_same_v<StringMap<int>::iterator::reference,
+                               StringMap<int>::iterator::value_type &>);
+
+  // Check that we can construct a const_iterator from an iterator.
+  static_assert(std::is_constructible_v<StringMap<int>::const_iterator,
+                                        StringMap<int>::iterator>);
+
+  // Double check that we can actually construct a const_iterator.
+  StringMap<int>::const_iterator const_it = Map.begin();
+  (void)const_it;
+}
+
+TEST(StringMapCustomTest, ConstIterator) {
+  StringMap<int> Map;
+  const StringMap<int> &ConstMap = Map;
+
+  // Check that ConstMap.begin() returns a const_iterator.
+  static_assert(std::is_same_v<decltype(ConstMap.begin()),
+                               StringMap<int>::const_iterator>);
+
+  // Check that the value_type of a const iterator is not a const type.
+  static_assert(
+      !std::is_const_v<StringMap<int>::const_iterator::value_type>,
+      "The value_type of a const iterator should not be a const type.");
+
+  // Check that pointer and reference types are const.
+  static_assert(
+      std::is_same_v<StringMap<int>::const_iterator::pointer,
+                     const StringMap<int>::const_iterator::value_type *>);
+  static_assert(
+      std::is_same_v<StringMap<int>::const_iterator::reference,
+                     const StringMap<int>::const_iterator::value_type &>);
+
+  // Check that we cannot construct an iterator from a const_iterator.
+  static_assert(!std::is_constructible_v<StringMap<int>::iterator,
+                                         StringMap<int>::const_iterator>);
+}
+
 } // end anonymous namespace


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to