[llvm-branch-commits] [clang] [BoundsSafety][NFC] Add the Sema/Parser bridge for late-parsed type attributes (PR #224551)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Sep 18 01:10:14 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Yeoul Na (rapidsna)

<details>
<summary>Changes</summary>

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.


---
Full diff: https://github.com/llvm/llvm-project/pull/224551.diff


5 Files Affected:

- (modified) clang/include/clang/Parse/Parser.h (+14) 
- (modified) clang/include/clang/Sema/Sema.h (+21) 
- (modified) clang/lib/Parse/ParseDecl.cpp (+32) 
- (modified) clang/lib/Parse/Parser.cpp (+5) 
- (modified) clang/lib/Sema/SemaType.cpp (+20) 


``````````diff
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index ce97ad25bcd16d..ae7c345ba6ce3e 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 8533f2823ed141..f643f1cb328d8d 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 9a49ce16447cc5..0ada18543ebd9e 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 c86ed6b2ea3f98..2d1fcffcf5e4e4 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 66e5c45d21d79c..a634569a99711b 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,

``````````

</details>


https://github.com/llvm/llvm-project/pull/224551


More information about the llvm-branch-commits mailing list