[llvm-branch-commits] [clang] [BoundsSafety][NFC] Thread a late-parsed attribute list through declarators (PR #224552)

Yeoul Na via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Sep 18 01:09:36 PDT 2026


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

A late-parsed type attribute is written in the middle of a declarator, so the
list it lands in has to travel with the declarator pieces until the enclosing
record can supply its argument. Add that storage and plumbing, with nothing
producing or consuming it yet:

  - Move CachedTokens and LateParsedAttrList earlier in DeclSpec.h so DeclSpec,
    Declarator and DeclaratorChunk can hold one.
  - Give DeclSpec, Declarator and DeclaratorChunk a LateParsedAttrList, and let
    Declarator::AddTypeInfo carry one onto the chunk it appends.
  - Give ParseSpecifierQualifierList and ParseTypeQualifierListOpt an optional
    LateParsedAttrList parameter, passed down to ParseDeclarationSpecifiers.

No functional change: the lists stay empty and no caller passes one. The next
commit populates them.


>From b23c50d2c31b8471910301a1f6c7b1c547778abc Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Thu, 17 Sep 2026 21:38:11 -0700
Subject: [PATCH] [BoundsSafety][NFC] Thread a late-parsed attribute list
 through declarators

A late-parsed type attribute is written in the middle of a declarator, so the
list it lands in has to travel with the declarator pieces until the enclosing
record can supply its argument. Add that storage and plumbing, with nothing
producing or consuming it yet:

  - Move CachedTokens and LateParsedAttrList earlier in DeclSpec.h so DeclSpec,
    Declarator and DeclaratorChunk can hold one.
  - Give DeclSpec, Declarator and DeclaratorChunk a LateParsedAttrList, and let
    Declarator::AddTypeInfo carry one onto the chunk it appends.
  - Give ParseSpecifierQualifierList and ParseTypeQualifierListOpt an optional
    LateParsedAttrList parameter, passed down to ParseDeclarationSpecifiers.

No functional change: the lists stay empty and no caller passes one. The next
commit populates them.
---
 clang/include/clang/Parse/Parser.h  | 23 +++++---
 clang/include/clang/Sema/DeclSpec.h | 87 ++++++++++++++++++-----------
 clang/lib/Parse/ParseDecl.cpp       | 15 ++++-
 3 files changed, 80 insertions(+), 45 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index ae7c345ba6ce3..6d0affa3c8822 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1953,10 +1953,12 @@ class Parser : public CodeCompletionHandler {
       DeclSpec &DS, AccessSpecifier AS, DeclSpecContext DSContext,
       LateParsedAttrList *LateAttrs = nullptr);
 
-  void ParseSpecifierQualifierList(
-      DeclSpec &DS, AccessSpecifier AS = AS_none,
-      DeclSpecContext DSC = DeclSpecContext::DSC_normal) {
-    ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC);
+  void
+  ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS = AS_none,
+                              DeclSpecContext DSC = DeclSpecContext::DSC_normal,
+                              LateParsedAttrList *LateAttrs = nullptr) {
+    ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC,
+                                LateAttrs);
   }
 
   /// ParseSpecifierQualifierList
@@ -1967,10 +1969,12 @@ class Parser : public CodeCompletionHandler {
   /// [GNU]    attributes     specifier-qualifier-list[opt]
   /// \endverbatim
   ///
-  void ParseSpecifierQualifierList(
-      DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
-      AccessSpecifier AS = AS_none,
-      DeclSpecContext DSC = DeclSpecContext::DSC_normal);
+  void
+  ParseSpecifierQualifierList(DeclSpec &DS,
+                              ImplicitTypenameContext AllowImplicitTypename,
+                              AccessSpecifier AS = AS_none,
+                              DeclSpecContext DSC = DeclSpecContext::DSC_normal,
+                              LateParsedAttrList *LateAttrs = nullptr);
 
   /// ParseEnumSpecifier
   /// \verbatim
@@ -2678,7 +2682,8 @@ class Parser : public CodeCompletionHandler {
   void ParseTypeQualifierListOpt(
       DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed,
       bool AtomicOrPtrauthAllowed = true, bool IdentifierRequired = false,
-      llvm::function_ref<void()> CodeCompletionHandler = {});
+      llvm::function_ref<void()> CodeCompletionHandler = {},
+      LateParsedAttrList *LateAttrs = nullptr);
 
   /// ParseDirectDeclarator
   /// \verbatim
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index e6dc6831d893f..9b1f3b244515d 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -213,6 +213,34 @@ namespace clang {
     unsigned location_size() const { return Builder.getBuffer().second; }
   };
 
+  /// A set of tokens that has been cached for later parsing.
+  typedef SmallVector<Token, 4> CachedTokens;
+
+  // A list of late-parsed attributes.  Used by ParseGNUAttributes.
+  class LateParsedAttrList : public SmallVector<LateParsedAttribute *, 2> {
+  public:
+    LateParsedAttrList(bool PSoon = false,
+                       bool LateAttrParseExperimentalExtOnly = false,
+                       bool LateAttrParseTypeAttrOnly = false)
+        : ParseSoon(PSoon),
+          LateAttrParseExperimentalExtOnly(LateAttrParseExperimentalExtOnly),
+          LateAttrParseTypeAttrOnly(LateAttrParseTypeAttrOnly) {}
+
+    bool parseSoon() const { return ParseSoon; }
+    /// returns true iff the attribute to be parsed should only be late parsed
+    /// if it is annotated with `LateAttrParseExperimentalExt`
+    bool lateAttrParseExperimentalExtOnly() const {
+      return LateAttrParseExperimentalExtOnly;
+    }
+
+    bool lateAttrParseTypeAttrOnly() const { return LateAttrParseTypeAttrOnly; }
+
+  private:
+    bool ParseSoon; // Are we planning to parse these shortly after creation?
+    bool LateAttrParseExperimentalExtOnly;
+    bool LateAttrParseTypeAttrOnly;
+  };
+
 /// Captures information about "declaration specifiers".
 ///
 /// "Declaration specifiers" encompasses storage-class-specifiers,
@@ -404,6 +432,9 @@ class DeclSpec {
   // attributes.
   ParsedAttributes Attrs;
 
+  // late attributes
+  LateParsedAttrList LateParsedAttrs;
+
   // Scope specifier for the type spec, if applicable.
   CXXScopeSpec TypeScope;
 
@@ -480,7 +511,9 @@ class DeclSpec {
         FS_virtual_specified(false), FS_noreturn_specified(false),
         FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>(
                                          ConstexprSpecKind::Unspecified)),
-        Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
+        Attrs(attrFactory), LateParsedAttrs(true, true, true), writtenBS(),
+
+        ObjCQualifiers(nullptr) {}
 
   // storage-class-specifier
   SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; }
@@ -880,6 +913,11 @@ class DeclSpec {
   ParsedAttributes &getAttributes() { return Attrs; }
   const ParsedAttributes &getAttributes() const { return Attrs; }
 
+  LateParsedAttrList &getLateAttributes() { return LateParsedAttrs; }
+  const LateParsedAttrList &getLateAttributes() const {
+    return LateParsedAttrs;
+  }
+
   void takeAttributesAppendingingFrom(ParsedAttributes &attrs) {
     Attrs.takeAllAppendingFrom(attrs);
   }
@@ -1252,40 +1290,12 @@ class UnqualifiedId {
   SourceLocation getEndLoc() const LLVM_READONLY { return EndLocation; }
 };
 
-/// A set of tokens that has been cached for later parsing.
-typedef SmallVector<Token, 4> CachedTokens;
-
-// A list of late-parsed attributes.  Used by ParseGNUAttributes.
-class LateParsedAttrList : public SmallVector<LateParsedAttribute *, 2> {
-public:
-  LateParsedAttrList(bool PSoon = false,
-                     bool LateAttrParseExperimentalExtOnly = false,
-                     bool LateAttrParseTypeAttrOnly = false)
-      : ParseSoon(PSoon),
-        LateAttrParseExperimentalExtOnly(LateAttrParseExperimentalExtOnly),
-        LateAttrParseTypeAttrOnly(LateAttrParseTypeAttrOnly) {}
-
-  bool parseSoon() const { return ParseSoon; }
-  /// returns true iff the attribute to be parsed should only be late parsed
-  /// if it is annotated with `LateAttrParseExperimentalExt`
-  bool lateAttrParseExperimentalExtOnly() const {
-    return LateAttrParseExperimentalExtOnly;
-  }
-
-  bool lateAttrParseTypeAttrOnly() const { return LateAttrParseTypeAttrOnly; }
-
-private:
-  bool ParseSoon; // Are we planning to parse these shortly after creation?
-  bool LateAttrParseExperimentalExtOnly;
-  bool LateAttrParseTypeAttrOnly;
-};
-
 /// One instance of this struct is used for each type in a
 /// declarator that is parsed.
 ///
 /// This is intended to be a small value object.
 struct DeclaratorChunk {
-  DeclaratorChunk() {};
+  DeclaratorChunk() : LateAttrList(true, true, true) {};
 
   enum {
     Pointer, Reference, Array, Function, BlockPointer, MemberPointer, Paren, Pipe
@@ -1303,6 +1313,7 @@ struct DeclaratorChunk {
   }
 
   ParsedAttributesView AttrList;
+  LateParsedAttrList LateAttrList;
 
   struct PointerTypeInfo {
     /// The type qualifiers: const/volatile/restrict/unaligned/atomic.
@@ -2015,6 +2026,8 @@ class Declarator {
   /// corresponding constructor parameter.
   const ParsedAttributesView &DeclarationAttrs;
 
+  LateParsedAttrList LateParsedAttrs;
+
   /// The asm label, if specified.
   Expr *AsmLabel;
 
@@ -2080,8 +2093,8 @@ class Declarator {
         Redeclaration(false), Extension(false), ObjCIvar(false),
         ObjCWeakProperty(false), InlineStorageUsed(false),
         HasInitializer(false), Attrs(DS.getAttributePool().getFactory()),
-        DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
-        TrailingRequiresClause(nullptr),
+        DeclarationAttrs(DeclarationAttrs), LateParsedAttrs(true, true, true),
+        AsmLabel(nullptr), TrailingRequiresClause(nullptr),
         InventedTemplateParameterList(nullptr) {
     assert(llvm::all_of(DeclarationAttrs,
                         [](const ParsedAttr &AL) {
@@ -2403,13 +2416,16 @@ class Declarator {
   /// This function takes attrs by R-Value reference because it takes ownership
   /// of those attributes from the parameter.
   void AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
-                   SourceLocation EndLoc) {
+                   SourceLocation EndLoc,
+                   const LateParsedAttrList &LateAttrs = {}) {
     DeclTypeInfo.push_back(TI);
     DeclTypeInfo.back().getAttrs().prepend(attrs.begin(), attrs.end());
     getAttributePool().takeAllFrom(attrs.getPool());
 
     if (!EndLoc.isInvalid())
       SetRangeEnd(EndLoc);
+
+    DeclTypeInfo.back().LateAttrList.append(LateAttrs);
   }
 
   /// AddTypeInfo - Add a chunk to this declarator. Also extend the range to
@@ -2739,6 +2755,11 @@ class Declarator {
     return DeclarationAttrs;
   }
 
+  LateParsedAttrList &getLateAttributes() { return LateParsedAttrs; }
+  const LateParsedAttrList &getLateAttributes() const {
+    return LateParsedAttrs;
+  }
+
   /// hasAttributes - do we contain any attributes?
   bool hasAttributes() const {
     if (!getAttributes().empty() || !getDeclarationAttributes().empty() ||
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 0ada18543ebd9..6b121119550b1 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2753,12 +2753,20 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
 
 void Parser::ParseSpecifierQualifierList(
     DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
-    AccessSpecifier AS, DeclSpecContext DSC) {
+    AccessSpecifier AS, DeclSpecContext DSC, LateParsedAttrList *LateAttrs) {
   ParsedTemplateInfo TemplateInfo;
+
+  if (LateAttrs)
+    assert(!std::any_of(LateAttrs->begin(), LateAttrs->end(),
+                        [&](const LateParsedAttribute *LA) {
+                          return isa<LateParsedTypeAttribute>(LA);
+                        }) &&
+           "Late type attribute carried over");
+
   /// specifier-qualifier-list is a subset of declaration-specifiers.  Just
   /// parse declaration-specifiers and complain about extra stuff.
   /// TODO: diagnose attribute-specifiers and alignment-specifiers.
-  ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, nullptr,
+  ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, LateAttrs,
                              AllowImplicitTypename);
 
   // Validate declspec for type-name.
@@ -6296,7 +6304,8 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
 
 void Parser::ParseTypeQualifierListOpt(
     DeclSpec &DS, unsigned AttrReqs, bool AtomicOrPtrauthAllowed,
-    bool IdentifierRequired, llvm::function_ref<void()> CodeCompletionHandler) {
+    bool IdentifierRequired, llvm::function_ref<void()> CodeCompletionHandler,
+    LateParsedAttrList *LateAttrs) {
   if ((AttrReqs & AR_CXX11AttributesParsed) &&
       isAllowedCXX11AttributeSpecifier()) {
     ParsedAttributes Attrs(AttrFactory);



More information about the llvm-branch-commits mailing list