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

<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 599e55022364f92c1809deae599248806873c1be Mon Sep 17 00:00:00 2001
From: Yeoul Na <[email protected]>
Date: Thu, 17 Sep 2026 21:12:37 -0700
Subject: [PATCH] [BoundsSafety][NFC] Add the Sema/Parser bridge for
 late-parsed type attributes

A late-parsed bounds attribute has to build its type when the attribute is
seen, but its argument isn't parseable until the enclosing record is complete.
Building that type needs the Parser (which owns the cached tokens) and Sema
(which owns type construction) to meet:

  - Sema::ActOnLateParsedTypeAttr validates a counted_by-family attribute for
    the type position and, if valid, wraps the type in a CountAttributedType
    whose count is not yet known, handing the node back for completion.

  - Parser::ProcessLateParsedTypeAttrCallback is the Parser-side entry point,
    registered on Sema so Sema can call back without including Parser.h (the
    same pattern as LateTemplateParserCallback). It reuses an already-built
    node so several declarators sharing one attribute share one type.

No functional change: nothing records late-parsed type attributes yet, so the
callback is never invoked. The next commit wires it up.
---
 clang/include/clang/Parse/Parser.h | 14 +++++++++++++
 clang/include/clang/Sema/Sema.h    | 21 ++++++++++++++++++++
 clang/lib/Parse/ParseDecl.cpp      | 32 ++++++++++++++++++++++++++++++
 clang/lib/Parse/Parser.cpp         |  5 +++++
 clang/lib/Sema/SemaType.cpp        | 20 +++++++++++++++++++
 5 files changed, 92 insertions(+)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index ce97ad25bcd16..ae7c345ba6ce3 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -8153,6 +8153,20 @@ class Parser : public CodeCompletionHandler {
 
   static void LateTemplateParserCallback(void *P, LateParsedTemplate &LPT);
 
+  /// Validate \p LA as a late-parsed type attribute and, if valid, wrap \p 
type
+  /// in a \c CountAttributedType whose count expression is not yet known,
+  /// recording the node on \p LA so it can be completed later.
+  ///
+  /// \p LA is downcast to \c LateParsedTypeAttribute; if the cast fails the
+  /// attribute is not applicable here and the function returns \c true to 
skip.
+  /// \p pointerNestLevel is the number of pointer/array/function declarator
+  /// chunks that precede the current chunk (see \c getPointerNestLevel).
+  /// Returns \c true on success and \c false if the attribute is invalid for
+  /// \p type.
+  static bool ProcessLateParsedTypeAttrCallback(LateParsedAttribute *LA,
+                                                QualType &type,
+                                                unsigned pointerNestLevel);
+
   /// We've parsed something that could plausibly be intended to be a template
   /// name (\p LHS) followed by a '<' token, and the following code can't
   /// possibly be an expression. Determine if this is likely to be a 
template-id
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 8533f2823ed14..f643f1cb328d8 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1354,6 +1354,27 @@ class Sema final : public SemaBase {
     OpaqueParser = P;
   }
 
+  /// Callback to the parser to interact with late-parsed type attributes. This
+  /// allows Sema to call back into Parser without including Parser.h.
+  ///
+  /// Processes a single late-parsed type attribute: validates the attribute
+  /// kind/type and wraps \p type in a CountAttributedType whose count is not 
yet
+  /// known, if appropriate. Returns false if the attribute is invalid.
+  typedef bool ProcessLateParsedTypeAttrCB(LateParsedAttribute *LA,
+                                           QualType &type,
+                                           unsigned pointerNestLevel);
+  ProcessLateParsedTypeAttrCB *ProcessLateParsedTypeAttrCallback = nullptr;
+
+  /// Called from the Parser's ProcessLateParsedTypeAttrCallback to validate a
+  /// counted_by-family attribute type and, if valid, wrap \p type in a
+  /// CountAttributedType whose count expression is not yet known. Returns 
false
+  /// if the attribute should be dropped, otherwise sets \p BATy to the node 
the
+  /// caller must complete once the argument is parseable.
+  bool ActOnLateParsedTypeAttr(ParsedAttr::Kind AttrKind,
+                               SourceLocation AttrNameLoc, QualType &type,
+                               unsigned pointerNestLevel,
+                               BoundsAttributedType **BATy);
+
   /// Callback to the parser to parse a type expressed as a string.
   std::function<TypeResult(StringRef, StringRef, SourceLocation)>
       ParseTypeFromStringCallback;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 9a49ce16447cc..0ada18543ebd9 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4896,6 +4896,38 @@ void 
Parser::ParseLexedTypeAttribute(LateParsedTypeAttribute &LA,
   OutAttrs.takeAllAppendingFrom(Attrs);
 }
 
+bool Parser::ProcessLateParsedTypeAttrCallback(LateParsedAttribute *LA,
+                                               QualType &type,
+                                               unsigned pointerNestLevel) {
+  auto *LTA = dyn_cast_if_present<LateParsedTypeAttribute>(LA);
+  if (!LTA)
+    return true;
+
+  // One attribute yields one type node, even when several declarators share 
it.
+  // A declaration-specifier-position attribute lives on the DeclSpec, whose
+  // late-attribute list ConvertDeclSpecToType walks once per declarator, so 
this
+  // callback runs N times for `IP __counted_by(n) a, b;`. Building a fresh
+  // (deliberately un-uniqued) node each time would leave every node but the 
last
+  // orphaned with a null count, so reuse the node instead. This matches the
+  // eager path, where getCountAttributedType uniques on the count expression 
and
+  // all declarators likewise share one node.
+  if (LTA->TypeToComplete) {
+    type = QualType(LTA->TypeToComplete, 0);
+    return true;
+  }
+
+  ParsedAttr::Kind AttrKind = ParsedAttr::getParsedKind(
+      &LTA->AttrName, nullptr, ParsedAttr::Form::GNU().getSyntax());
+  // Sema cannot see LateParsedTypeAttribute's definition, so it hands the node
+  // back and we record it here for the completion pass to fill in.
+  BoundsAttributedType *BATy = nullptr;
+  if (!LTA->Self->Actions.ActOnLateParsedTypeAttr(
+          AttrKind, LTA->AttrNameLoc, type, pointerNestLevel, &BATy))
+    return false;
+  LTA->TypeToComplete = BATy;
+  return true;
+}
+
 void Parser::CompleteLateParsedTypeAttributes(
     SmallVectorImpl<LateParsedTypeAttribute *> &LateTypeAttrs) {
   for (LateParsedTypeAttribute *LTA : LateTypeAttrs) {
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index c86ed6b2ea3f9..2d1fcffcf5e4e 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -592,6 +592,11 @@ void Parser::Initialize() {
   }
 
   Actions.Initialize();
+  // Register the callback so Sema can call back into the Parser to handle
+  // late-parsed type attributes (e.g. counted_by on struct fields), which
+  // may be processed at any point during parsing via ActOnFields.
+  Actions.ProcessLateParsedTypeAttrCallback =
+      &Parser::ProcessLateParsedTypeAttrCallback;
 
   // Prime the lexer look-ahead.
   ConsumeToken();
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 66e5c45d21d79..a634569a99711 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -9124,6 +9124,26 @@ static bool validateBoundsAttrTypeForTypePosition(
   return true;
 }
 
+bool Sema::ActOnLateParsedTypeAttr(ParsedAttr::Kind AttrKind,
+                                   SourceLocation AttrNameLoc, QualType &type,
+                                   unsigned pointerNestLevel,
+                                   BoundsAttributedType **BATy) {
+  BoundsAttrFlags Flags;
+  if (!validateBoundsAttrTypeForTypePosition(*this, type, AttrKind, 
AttrNameLoc,
+                                             SourceRange(AttrNameLoc),
+                                             pointerNestLevel, Flags))
+    return false;
+
+  // The argument hasn't been parsed yet, so build the type without it and hand
+  // the node back for completion. Because enclosing types refer to it by
+  // pointer, filling the argument in later leaves them untouched — no rebuild
+  // of the type chain and no TypeLoc re-emission.
+  auto *CATy = getASTContext().getIncompleteCountAttributedType(
+      type, Flags.CountInBytes, Flags.OrNull);
+  type = QualType(CATy, 0);
+  *BATy = CATy;
+  return true;
+}
 
 static void processTypeAttrs(TypeProcessingState &state, QualType &type,
                              TypeAttrLocation TAL,

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

Reply via email to