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

<sub>Stack created with <a href="https://github.com/github/gh-stack";>GitHub 
Stacks CLI</a> • <a href="https://gh.io/stacks-feedback";>Give Feedback 
💬</a></sub>

>From 9be681ae5916c3562b80ac99990b6c99e844a30e Mon Sep 17 00:00:00 2001
From: Yeoul Na <[email protected]>
Date: Thu, 10 Sep 2026 07:07:26 -0700
Subject: [PATCH] [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 8c1418625bf33..4b931d1836dec 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -4924,7 +4924,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 483f9ab088799..1026e5e938137 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -10001,6 +10001,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,

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

Reply via email to