[clang] [Clang] Fix quadratic designated-initializer checking (PR #225573)

via cfe-commits cfe-commits at lists.llvm.org
Sat Sep 26 20:52:29 PDT 2026


https://github.com/AnonMiraj updated https://github.com/llvm/llvm-project/pull/225573

>From ab07603afbfac0f24f48e64bfcf66ff2324a31ed Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Tue, 22 Sep 2026 02:36:26 +0300
Subject: [PATCH 1/2] [Clang] Fix quadratic designated-initializer checking

---
 clang/lib/Sema/SemaInit.cpp | 32 +++++++++++++++++++++++++-------
 1 file changed, 25 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 8f685feac4beb..bdb2b3fedb642 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -363,6 +363,10 @@ class InitListChecker {
   SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr;
   EmbedExpr *CurEmbed = nullptr; // Save current embed we're processing.
   unsigned CurEmbedIndex = 0;
+  /// Indices of a record's unnamed bitfields, in increasing order. Usually
+  /// empty. getFieldIndex() counts them, designators don't.
+  llvm::SmallDenseMap<const RecordDecl *, SmallVector<unsigned, 0>, 2>
+      UnnamedBitFieldIndices;
 
   NoInitExpr *getDummyInit() {
     if (!DummyExpr)
@@ -3012,14 +3016,28 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
 
     unsigned FieldIndex = NumBases;
 
-    for (auto *FI : RD->fields()) {
-      if (FI->isUnnamedBitField())
-        continue;
-      if (declaresSameEntity(KnownField, FI)) {
-        KnownField = FI;
-        break;
+    // Avoid a quadratic per-designator scan; the AST caches each field's
+    // index.
+    if (KnownField->getParent() == RD) {
+      auto [It, Inserted] = UnnamedBitFieldIndices.try_emplace(RD);
+      if (Inserted)
+        for (const FieldDecl *FI : RD->fields())
+          if (FI->isUnnamedBitField())
+            It->second.push_back(FI->getFieldIndex());
+      unsigned Index = KnownField->getFieldIndex();
+      FieldIndex +=
+          Index - (llvm::lower_bound(It->second, Index) - It->second.begin());
+    } else {
+      // A field of another record: its cached index isn't RD's numbering.
+      for (auto *FI : RD->fields()) {
+        if (FI->isUnnamedBitField())
+          continue;
+        if (declaresSameEntity(KnownField, FI)) {
+          KnownField = FI;
+          break;
+        }
+        ++FieldIndex;
       }
-      ++FieldIndex;
     }
 
     RecordDecl::field_iterator Field =

>From c59a6da4552f848a360541cd5875b60f56439071 Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Fri, 25 Sep 2026 02:29:18 +0300
Subject: [PATCH 2/2] Move the designator field index lookup to ASTContext

---
 clang/include/clang/AST/ASTContext.h |  9 +++++++++
 clang/lib/AST/ASTContext.cpp         | 15 +++++++++++++++
 clang/lib/Sema/SemaInit.cpp          | 16 +++-------------
 3 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index a4ed852d36442..5ccf885b8a6e7 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -330,6 +330,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// This is lazily created.  This is intentionally not serialized.
   mutable llvm::DenseMap<const RecordDecl*, const ASTRecordLayout*>
     ASTRecordLayouts;
+  /// Indices of a record's unnamed bitfields, in increasing order. Usually
+  /// empty. getFieldIndex() counts them, designators don't.
+  mutable llvm::DenseMap<const RecordDecl *, llvm::SmallVector<unsigned, 0>>
+      UnnamedBitFieldIndices;
   mutable llvm::DenseMap<const ObjCInterfaceDecl *, const ASTRecordLayout *>
       ObjCLayouts;
 
@@ -2875,6 +2879,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// position information.
   const ASTRecordLayout &getASTRecordLayout(const RecordDecl *D) const;
 
+  /// Returns the index of Field among RD's named fields, the numbering
+  /// designators use.
+  std::optional<unsigned> getFieldIndex(const RecordDecl *RD,
+                                        const FieldDecl *Field) const;
+
   /// Get or compute information about the layout of the specified
   /// Objective-C interface.
   const ASTRecordLayout &getASTObjCInterfaceLayout(const ObjCInterfaceDecl *D)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index abf0cd5e18c2b..8b6233ebdc8cd 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3236,6 +3236,21 @@ ASTContext::getASTObjCInterfaceLayout(const ObjCInterfaceDecl *D) const {
   return getObjCLayout(D);
 }
 
+std::optional<unsigned>
+ASTContext::getFieldIndex(const RecordDecl *RD, const FieldDecl *Field) const {
+  if (Field->getParent() != RD)
+    return std::nullopt;
+
+  // Designators skip unnamed bitfields; subtract the ones before Field.
+  auto [It, Inserted] = UnnamedBitFieldIndices.try_emplace(RD);
+  if (Inserted)
+    for (const FieldDecl *FI : RD->fields())
+      if (FI->isUnnamedBitField())
+        It->second.push_back(FI->getFieldIndex());
+  unsigned Index = Field->getFieldIndex();
+  return Index - (llvm::lower_bound(It->second, Index) - It->second.begin());
+}
+
 static auto getCanonicalTemplateArguments(const ASTContext &C,
                                           ArrayRef<TemplateArgument> Args,
                                           bool &AnyNonCanonArgs) {
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index bdb2b3fedb642..6e1ddcb6a7cab 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -363,10 +363,6 @@ class InitListChecker {
   SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr;
   EmbedExpr *CurEmbed = nullptr; // Save current embed we're processing.
   unsigned CurEmbedIndex = 0;
-  /// Indices of a record's unnamed bitfields, in increasing order. Usually
-  /// empty. getFieldIndex() counts them, designators don't.
-  llvm::SmallDenseMap<const RecordDecl *, SmallVector<unsigned, 0>, 2>
-      UnnamedBitFieldIndices;
 
   NoInitExpr *getDummyInit() {
     if (!DummyExpr)
@@ -3018,15 +3014,9 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
 
     // Avoid a quadratic per-designator scan; the AST caches each field's
     // index.
-    if (KnownField->getParent() == RD) {
-      auto [It, Inserted] = UnnamedBitFieldIndices.try_emplace(RD);
-      if (Inserted)
-        for (const FieldDecl *FI : RD->fields())
-          if (FI->isUnnamedBitField())
-            It->second.push_back(FI->getFieldIndex());
-      unsigned Index = KnownField->getFieldIndex();
-      FieldIndex +=
-          Index - (llvm::lower_bound(It->second, Index) - It->second.begin());
+    if (std::optional<unsigned> SelfIndex =
+            SemaRef.Context.getFieldIndex(RD, KnownField)) {
+      FieldIndex += *SelfIndex;
     } else {
       // A field of another record: its cached index isn't RD's numbering.
       for (auto *FI : RD->fields()) {



More information about the cfe-commits mailing list