https://github.com/rapidsna updated 
https://github.com/llvm/llvm-project/pull/224556

>From 6243991b418f4d8c5a95e418d35093bf261f7d9a Mon Sep 17 00:00:00 2001
From: Yeoul Na <[email protected]>
Date: Thu, 10 Sep 2026 07:07:26 -0700
Subject: [PATCH 1/4] [BoundsSafety][NFC] Add the count-refill logic for
 late-parsed bounds attributes

Introduce the machinery that fills in a late-parsed bounds attribute's
argument once the enclosing record is complete, without wiring it up yet.
Nothing creates an incomplete CountAttributedType or records one for
completion at this point, so this is inert -- the functions are unused and
behavior is unchanged. Activation follows in the next commit.
---
 clang/include/clang/Parse/Parser.h | 15 +++++++++
 clang/include/clang/Sema/Sema.h    |  7 ++++
 clang/lib/AST/Decl.cpp             |  8 ++++-
 clang/lib/Parse/ParseDecl.cpp      | 48 ++++++++++++++++++++++++++
 clang/lib/Sema/SemaType.cpp        | 54 ++++++++++++++++++++++++++++++
 5 files changed, 131 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index e9bab81b095fd..ce97ad25bcd16 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -232,6 +232,16 @@ struct LateParsedAttribute : public LateParsedDeclaration {
 /// is replaced with a concrete type (e.g., CountAttributedType).
 struct LateParsedTypeAttribute : public LateParsedAttribute {
 
+  /// The type built for this attribute during type construction, still missing
+  /// the argument that hasn't been parsed yet. Filled in by
+  /// `Parser::ProcessLateParsedTypeAttrCallback` and completed once the
+  /// enclosing scope makes the argument parseable. Null if type construction
+  /// rejected the attribute.
+  ///
+  /// Held as the base class so the parser stays agnostic about which bounds
+  /// attribute this is; Sema dispatches on the concrete kind when completing.
+  BoundsAttributedType *TypeToComplete = nullptr;
+
   explicit LateParsedTypeAttribute(Parser *P, IdentifierInfo &Name,
                                    SourceLocation Loc)
       : LateParsedAttribute(P, Name, Loc, Kind::Type) {}
@@ -1524,6 +1534,11 @@ class Parser : public CodeCompletionHandler {
   void ParseLexedTypeAttribute(LateParsedTypeAttribute &LA,
                                ParsedAttributes &OutAttrs);
 
+  /// Complete every late-parsed type attribute queued for the record whose 
body
+  /// just closed. Consumes and clears \p LateTypeAttrs.
+  void CompleteLateParsedTypeAttributes(
+      SmallVectorImpl<LateParsedTypeAttribute *> &LateTypeAttrs);
+
   /// Parse cached tokens for a late-parsed attribute and return the parsed
   /// attributes. Shared implementation used by both ParseLexedAttribute and
   /// ParseLexedTypeAttribute.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5becfc9fae152..46df36396bc6f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2514,6 +2514,13 @@ class Sema final : public SemaBase {
   bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
                                  bool OrNull);
 
+  /// Supply the parsed argument of a late-parsed bounds attribute to the type
+  /// built for it by ActOnLateParsedTypeAttr, and run the checks that need the
+  /// owning declaration. \p FD is the field the type belongs to. Returns false
+  /// if the attribute was rejected.
+  bool ActOnLateParsedTypeAttrArgument(BoundsAttributedType *BATy,
+                                       FieldDecl *FD, Expr *Arg);
+
   /// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` 
or
   /// `__counted_by_or_null` pointer type \param LHSTy.
   ///
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index ffd9bd33c7501..b145560508e95 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -4922,7 +4922,13 @@ const FieldDecl *FieldDecl::findCountedByField() const {
   if (!CAT)
     return nullptr;
 
-  const auto *CountDRE = cast<DeclRefExpr>(CAT->getCountExpr());
+  // A late-parsed attribute whose argument was rejected keeps the node with 
the
+  // raw argument as its count (see Sema::ActOnLateParsedTypeAttrArgument). 
That
+  // argument may not be a simple declaration reference (e.g. it may be an 
error
+  // expression or a `sizeof`), in which case it refers to no field.
+  const auto *CountDRE = dyn_cast<DeclRefExpr>(CAT->getCountExpr());
+  if (!CountDRE)
+    return nullptr;
   const auto *CountDecl = CountDRE->getDecl();
   if (const auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl))
     CountDecl = IFD->getAnonField();
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 5976f5a7ccdea..9a49ce16447cc 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4896,6 +4896,54 @@ void 
Parser::ParseLexedTypeAttribute(LateParsedTypeAttribute &LA,
   OutAttrs.takeAllAppendingFrom(Attrs);
 }
 
+void Parser::CompleteLateParsedTypeAttributes(
+    SmallVectorImpl<LateParsedTypeAttribute *> &LateTypeAttrs) {
+  for (LateParsedTypeAttribute *LTA : LateTypeAttrs) {
+    // Read these out before parsing, which destroys the attribute. The type is
+    // null if construction rejected the attribute, in which case the 
diagnostic
+    // has already been emitted and there is nothing to complete.
+    BoundsAttributedType *BATy = LTA->TypeToComplete;
+    // Rejected during construction (already diagnosed); the cached tokens are
+    // self-contained, so there is nothing to drain — just discard it.
+    if (!BATy) {
+      delete LTA;
+      continue;
+    }
+    // The fields were
+    // attached in ParseStructDeclaration as each declarator was completed; 
more
+    // than one appears when several declarators share a
+    // declaration-specifier-position attribute.
+    SmallVector<Decl *, 2> Fields(LTA->Decls);
+
+    AttributeFactory AF;
+    ParsedAttributes Attrs(AF);
+    ParseLexedTypeAttribute(*LTA, Attrs);
+    delete LTA;
+
+    // An unparseable argument leaves no attribute behind; already diagnosed.
+    if (Attrs.empty())
+      continue;
+    assert(Attrs.size() == 1);
+
+    Expr *Arg = Attrs[0].getArgAsExpr(0);
+    assert(Arg);
+
+    // No field means the attribute never reached a field declarator (for
+    // instance the type was rejected during construction, which unwraps the
+    // node and leaves it unreferenced), so nothing is left to complete.
+    bool Valid = !Fields.empty();
+    for (Decl *FD : Fields)
+      Valid &= Actions.ActOnLateParsedTypeAttrArgument(
+          BATy, cast<FieldDecl>(FD), Arg);
+
+    if (Valid)
+      Attrs[0].setUsedAsTypeAttr();
+    else
+      Attrs[0].setInvalid();
+  }
+  LateTypeAttrs.clear();
+}
+
 void LateParsedTypeAttribute::ParseInto(ParsedAttributes &OutAttrs) {
   // Delegate to the Parser that created this attribute
   Self->ParseLexedTypeAttribute(*this, OutAttrs);
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 2796ac2929f46..258895ae24a82 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -10008,6 +10008,60 @@ BuildTypeCoupledDecls(Expr *E,
   Decls.push_back(TypeCoupledDeclRefInfo(CountDecl, /*IsDref*/ false));
 }
 
+bool Sema::ActOnLateParsedTypeAttrArgument(BoundsAttributedType *BATy,
+                                           FieldDecl *FD, Expr *Arg) {
+  assert(Arg);
+
+  // Only the counted_by family exists so far.
+  auto *CATy = cast<CountAttributedType>(BATy);
+
+  // A nested counted_by (buried under a pointer or array) was diagnosed and
+  // dropped to its wrapped type while the declarator was built, orphaning this
+  // node -- it is no longer part of the field's type. getAs finds only a
+  // top-level (through-sugar) CountAttributedType, so when it can't find this
+  // node the node was dropped: skip it, leaving the field as-is. This matches
+  // the eager path, which drops the attribute for a nested counted_by.
+  if (FD->getType()->getAs<CountAttributedType>() != CATy)
+    return false;
+
+  // Rejected: complete the node in place with the raw argument and no coupled
+  // decls. The argument isn't a valid count reference, so there are none --
+  // and BuildTypeCoupledDecls would assert on a non-DeclRefExpr. Mark the 
field
+  // invalid; consumers bail on a non-DeclRefExpr count. Guarded so shared
+  // declarators (`IP __counted_by(n) a, b;`) only complete the node once.
+  auto Reject = [&]() -> bool {
+    if (!CATy->getCountExpr())
+      Context.completeCountAttributedType(CATy, Arg, {});
+    FD->setInvalidDecl();
+    return false;
+  };
+
+  // A failed parse was already diagnosed; skip the checks (they would only add
+  // noise) and recover the node directly.
+  if (Arg->containsErrors())
+    return Reject();
+
+  // Rejected (diagnostic emitted by the check): a bad count expression, or a
+  // valid reference in an invalid position (union member, non-flexible array,
+  // cross-struct count).
+  if (CheckCountedByAttrOnField(FD, Arg, CATy->isCountInBytes(),
+                                CATy->isOrNull()))
+    return Reject();
+
+  // Valid: the argument is a simple declaration reference, so it's safe to
+  // derive the coupled decls. Several declarators can share one node when the
+  // attribute was written in declaration-specifier position
+  // (`IP __counted_by(n) a, b;`), so this runs once per field; completion is
+  // idempotent -- the first field supplies the count, the rest only need the
+  // decl-context check above.
+  llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
+  BuildTypeCoupledDecls(Arg, Decls);
+  if (!CATy->getCountExpr())
+    Context.completeCountAttributedType(CATy, Arg, Decls);
+
+  return true;
+}
+
 QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
                                                       Expr *CountExpr,
                                                       bool CountInBytes,

>From 914adf6d18226e52a2cf2e440c6e56c45073c7ca Mon Sep 17 00:00:00 2001
From: Yeoul Na <[email protected]>
Date: Fri, 18 Sep 2026 09:54:36 -0700
Subject: [PATCH 2/4] Use unique_ptr and remove raw deletes; assertions instead
 of bail out

---
 clang/lib/Parse/ParseDecl.cpp | 31 ++++++++++---------------------
 1 file changed, 10 insertions(+), 21 deletions(-)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 9a49ce16447cc..848390205b983 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4898,27 +4898,18 @@ void 
Parser::ParseLexedTypeAttribute(LateParsedTypeAttribute &LA,
 
 void Parser::CompleteLateParsedTypeAttributes(
     SmallVectorImpl<LateParsedTypeAttribute *> &LateTypeAttrs) {
-  for (LateParsedTypeAttribute *LTA : LateTypeAttrs) {
-    // Read these out before parsing, which destroys the attribute. The type is
-    // null if construction rejected the attribute, in which case the 
diagnostic
-    // has already been emitted and there is nothing to complete.
+  for (LateParsedTypeAttribute *RawLTA : LateTypeAttrs) {
+    std::unique_ptr<LateParsedTypeAttribute> LTA(RawLTA);
+
     BoundsAttributedType *BATy = LTA->TypeToComplete;
-    // Rejected during construction (already diagnosed); the cached tokens are
-    // self-contained, so there is nothing to drain — just discard it.
-    if (!BATy) {
-      delete LTA;
+    if (!BATy)
       continue;
-    }
-    // The fields were
-    // attached in ParseStructDeclaration as each declarator was completed; 
more
-    // than one appears when several declarators share a
-    // declaration-specifier-position attribute.
-    SmallVector<Decl *, 2> Fields(LTA->Decls);
+
+    ArrayRef<Decl *> Fields = LTA->Decls;
 
     AttributeFactory AF;
     ParsedAttributes Attrs(AF);
     ParseLexedTypeAttribute(*LTA, Attrs);
-    delete LTA;
 
     // An unparseable argument leaves no attribute behind; already diagnosed.
     if (Attrs.empty())
@@ -4928,13 +4919,11 @@ void Parser::CompleteLateParsedTypeAttributes(
     Expr *Arg = Attrs[0].getArgAsExpr(0);
     assert(Arg);
 
-    // No field means the attribute never reached a field declarator (for
-    // instance the type was rejected during construction, which unwraps the
-    // node and leaves it unreferenced), so nothing is left to complete.
-    bool Valid = !Fields.empty();
+    bool Valid = true;
+    assert(!Fields.empty());
     for (Decl *FD : Fields)
-      Valid &= Actions.ActOnLateParsedTypeAttrArgument(
-          BATy, cast<FieldDecl>(FD), Arg);
+      Valid &= Actions.ActOnLateParsedTypeAttrArgument(BATy, 
cast<FieldDecl>(FD),
+                                                       Arg);
 
     if (Valid)
       Attrs[0].setUsedAsTypeAttr();

>From 35f5df90d6cd5e621aafa6bf4fbe676728451140 Mon Sep 17 00:00:00 2001
From: Yeoul Na <[email protected]>
Date: Sat, 19 Sep 2026 07:49:07 -0700
Subject: [PATCH 3/4] Record rejected nodes explicitly and skip; trim wordy
 comments

---
 clang/include/clang/Sema/Sema.h | 15 +++++++++++++++
 clang/lib/Parse/ParseDecl.cpp   |  2 +-
 clang/lib/Sema/SemaType.cpp     | 30 +++++-------------------------
 3 files changed, 21 insertions(+), 26 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 46df36396bc6f..03fc536e4ec6e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2514,6 +2514,21 @@ class Sema final : public SemaBase {
   bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
                                  bool OrNull);
 
+  /// Late-parsed bounds types dropped while their declarator was built. The
+  /// attribute has already been diagnosed and its node is no longer part of
+  /// any type, so the completion pass must skip it rather than parse its
+  /// argument and complete it.
+  llvm::SmallPtrSet<const BoundsAttributedType *, 4>
+      RejectedLateParsedBoundsTypes;
+
+  void markLateParsedBoundsTypeRejected(const BoundsAttributedType *BATy) {
+    RejectedLateParsedBoundsTypes.insert(BATy);
+  }
+
+  bool isLateParsedBoundsTypeRejected(const BoundsAttributedType *BATy) const {
+    return RejectedLateParsedBoundsTypes.contains(BATy);
+  }
+
   /// Supply the parsed argument of a late-parsed bounds attribute to the type
   /// built for it by ActOnLateParsedTypeAttr, and run the checks that need the
   /// owning declaration. \p FD is the field the type belongs to. Returns false
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 848390205b983..e3b0eda1af670 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4902,7 +4902,7 @@ void Parser::CompleteLateParsedTypeAttributes(
     std::unique_ptr<LateParsedTypeAttribute> LTA(RawLTA);
 
     BoundsAttributedType *BATy = LTA->TypeToComplete;
-    if (!BATy)
+    if (!BATy || Actions.isLateParsedBoundsTypeRejected(BATy))
       continue;
 
     ArrayRef<Decl *> Fields = LTA->Decls;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 258895ae24a82..3dcd330c52c83 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -10015,47 +10015,27 @@ bool 
Sema::ActOnLateParsedTypeAttrArgument(BoundsAttributedType *BATy,
   // Only the counted_by family exists so far.
   auto *CATy = cast<CountAttributedType>(BATy);
 
-  // A nested counted_by (buried under a pointer or array) was diagnosed and
-  // dropped to its wrapped type while the declarator was built, orphaning this
-  // node -- it is no longer part of the field's type. getAs finds only a
-  // top-level (through-sugar) CountAttributedType, so when it can't find this
-  // node the node was dropped: skip it, leaving the field as-is. This matches
-  // the eager path, which drops the attribute for a nested counted_by.
-  if (FD->getType()->getAs<CountAttributedType>() != CATy)
-    return false;
-
-  // Rejected: complete the node in place with the raw argument and no coupled
-  // decls. The argument isn't a valid count reference, so there are none --
-  // and BuildTypeCoupledDecls would assert on a non-DeclRefExpr. Mark the 
field
-  // invalid; consumers bail on a non-DeclRefExpr count. Guarded so shared
-  // declarators (`IP __counted_by(n) a, b;`) only complete the node once.
   auto Reject = [&]() -> bool {
+    // Guarded so shared declarators (`IP __counted_by(n) a, b;`) only complete
+    // the node once.
     if (!CATy->getCountExpr())
       Context.completeCountAttributedType(CATy, Arg, {});
     FD->setInvalidDecl();
     return false;
   };
 
-  // A failed parse was already diagnosed; skip the checks (they would only add
-  // noise) and recover the node directly.
   if (Arg->containsErrors())
     return Reject();
 
-  // Rejected (diagnostic emitted by the check): a bad count expression, or a
-  // valid reference in an invalid position (union member, non-flexible array,
-  // cross-struct count).
   if (CheckCountedByAttrOnField(FD, Arg, CATy->isCountInBytes(),
                                 CATy->isOrNull()))
     return Reject();
 
-  // Valid: the argument is a simple declaration reference, so it's safe to
-  // derive the coupled decls. Several declarators can share one node when the
-  // attribute was written in declaration-specifier position
-  // (`IP __counted_by(n) a, b;`), so this runs once per field; completion is
-  // idempotent -- the first field supplies the count, the rest only need the
-  // decl-context check above.
   llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
   BuildTypeCoupledDecls(Arg, Decls);
+  // Several declarators can share one node when the attribute was written in
+  // declaration-specifier position (`IP __counted_by(n) a, b;`), so this runs
+  // once per field
   if (!CATy->getCountExpr())
     Context.completeCountAttributedType(CATy, Arg, Decls);
 

>From e419bb5638d95b3eafd8993a5e25d304c6b5c428 Mon Sep 17 00:00:00 2001
From: Yeoul Na <[email protected]>
Date: Sat, 19 Sep 2026 10:49:09 -0700
Subject: [PATCH 4/4] clang format

---
 clang/lib/Parse/ParseDecl.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index e3b0eda1af670..de97611e8bcae 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4922,8 +4922,8 @@ void Parser::CompleteLateParsedTypeAttributes(
     bool Valid = true;
     assert(!Fields.empty());
     for (Decl *FD : Fields)
-      Valid &= Actions.ActOnLateParsedTypeAttrArgument(BATy, 
cast<FieldDecl>(FD),
-                                                       Arg);
+      Valid &= Actions.ActOnLateParsedTypeAttrArgument(
+          BATy, cast<FieldDecl>(FD), Arg);
 
     if (Valid)
       Attrs[0].setUsedAsTypeAttr();

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to