Author: Yeoul Na
Date: 2026-11-03T10:46:22-08:00
New Revision: a23f991b4e3c856d89cc7424a1efa908562a1376

URL: 
https://github.com/llvm/llvm-project/commit/a23f991b4e3c856d89cc7424a1efa908562a1376
DIFF: 
https://github.com/llvm/llvm-project/commit/a23f991b4e3c856d89cc7424a1efa908562a1376.diff

LOG: [BoundsSafety][NFC] Add late-parsed type attribute lists to declarators 
(#224559)

This is groundwork for late-parsing type attributes in every type
position where an ordinary type attribute can appear, following the
direction set in https://github.com/llvm/llvm-project/pull/223267, which
allows a CountAttributedType's count to be filled in later.

Ordinary type attributes are kept on the DeclSpec, on each
DeclaratorChunk and on the
Declarator. Give late-parsed type attributes the same three homes, with
nothing producing or consuming them yet.

No functional change: no caller passes a list yet, so the lists stay
empty. The next commit populates them.

Assisted-by: Opus 5.5

<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>

Added: 
    

Modified: 
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/DeclSpec.h
    clang/lib/Parse/ParseDecl.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index b19f0ed880ddfe..e4fa25ebc427f6 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
@@ -2677,7 +2681,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 7b58bfcf53d98a..a1fd6c8ef6adc8 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -214,6 +214,64 @@ 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) {}
+
+    /// A list for the late-parsed type attributes of a declaration specifier,
+    /// declarator or declarator chunk.
+    static LateParsedAttrList forTypeAttrs() {
+      return LateParsedAttrList(/*PSoon=*/true,
+                                /*LateAttrParseExperimentalExtOnly=*/true,
+                                /*LateAttrParseTypeAttrOnly=*/true);
+    }
+
+    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;
+  };
+
+  /// The part of a late-parsed type attribute that Sema reads and fills in
+  /// during type construction. The parser owns the attribute and its cached
+  /// tokens and embeds this, so Sema never needs to see the parser's type.
+  struct LateParsedTypeAttrInfo {
+    ParsedAttr::Kind Kind;
+    SourceLocation NameLoc;
+
+    /// The type built for this attribute during type construction, still
+    /// missing the argument that hasn't been parsed yet. Filled in by
+    /// `Sema::ProcessLateParsedTypeAttr` and completed once the enclosing 
scope
+    /// makes the argument parseable. Null if type construction rejected the
+    /// attribute.
+    ///
+    /// Store pointer to 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;
+
+    LateParsedTypeAttrInfo(ParsedAttr::Kind Kind, SourceLocation NameLoc)
+        : Kind(Kind), NameLoc(NameLoc) {}
+  };
+
 /// Captures information about "declaration specifiers".
 ///
 /// "Declaration specifiers" encompasses storage-class-specifiers,
@@ -408,6 +466,9 @@ class DeclSpec {
   // attributes.
   ParsedAttributes Attrs;
 
+  // late attributes
+  LateParsedAttrList LateParsedAttrs;
+
   // Scope specifier for the type spec, if applicable.
   CXXScopeSpec TypeScope;
 
@@ -484,7 +545,8 @@ 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(LateParsedAttrList::forTypeAttrs()),
+        writtenBS(), ObjCQualifiers(nullptr) {}
 
   // storage-class-specifier
   SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; }
@@ -884,6 +946,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);
   }
@@ -1256,61 +1323,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;
-};
-
-/// The part of a late-parsed type attribute that Sema reads and fills in 
during
-/// type construction. The parser owns the attribute and its cached tokens and
-/// embeds this, so Sema never needs to see the parser's type.
-struct LateParsedTypeAttrInfo {
-  ParsedAttr::Kind Kind;
-  SourceLocation NameLoc;
-
-  /// The type built for this attribute during type construction, still missing
-  /// the argument that hasn't been parsed yet. Filled in by
-  /// `Sema::ProcessLateParsedTypeAttr` 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;
-
-  LateParsedTypeAttrInfo(ParsedAttr::Kind Kind, SourceLocation NameLoc)
-      : Kind(Kind), NameLoc(NameLoc) {}
-};
-
 /// 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(LateParsedAttrList::forTypeAttrs()) {}
 
   enum {
     Pointer, Reference, Array, Function, BlockPointer, MemberPointer, Paren, 
Pipe
@@ -1328,6 +1346,7 @@ struct DeclaratorChunk {
   }
 
   ParsedAttributesView AttrList;
+  LateParsedAttrList LateAttrList;
 
   struct PointerTypeInfo {
     /// The type qualifiers: const/volatile/restrict/unaligned/atomic.
@@ -2040,6 +2059,8 @@ class Declarator {
   /// corresponding constructor parameter.
   const ParsedAttributesView &DeclarationAttrs;
 
+  LateParsedAttrList LateParsedAttrs;
+
   /// The asm label, if specified.
   Expr *AsmLabel;
 
@@ -2105,7 +2126,8 @@ class Declarator {
         Redeclaration(false), Extension(false), ObjCIvar(false),
         ObjCWeakProperty(false), InlineStorageUsed(false),
         HasInitializer(false), Attrs(DS.getAttributePool().getFactory()),
-        DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
+        DeclarationAttrs(DeclarationAttrs),
+        LateParsedAttrs(LateParsedAttrList::forTypeAttrs()), AsmLabel(nullptr),
         TrailingRequiresClause(nullptr),
         InventedTemplateParameterList(nullptr) {
     assert(llvm::all_of(DeclarationAttrs,
@@ -2194,6 +2216,7 @@ class Declarator {
       DeclTypeInfo[i].destroy();
     DeclTypeInfo.clear();
     Attrs.clear();
+    LateParsedAttrs.clear();
     AsmLabel = nullptr;
     InlineStorageUsed = false;
     HasInitializer = false;
@@ -2428,13 +2451,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
@@ -2764,6 +2790,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 e452c312702491..03252922458d04 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2768,12 +2768,17 @@ Decl 
*Parser::ParseDeclarationAfterDeclaratorAndAttributes(
 
 void Parser::ParseSpecifierQualifierList(
     DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
-    AccessSpecifier AS, DeclSpecContext DSC) {
+    AccessSpecifier AS, DeclSpecContext DSC, LateParsedAttrList *LateAttrs) {
   ParsedTemplateInfo TemplateInfo;
+
+  assert((!LateAttrs ||
+          llvm::none_of(*LateAttrs, llvm::IsaPred<LateParsedTypeAttribute>)) &&
+         "caller's late-attribute list must not hold type attributes yet");
+
   /// 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.
@@ -6326,7 +6331,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);
@@ -6494,7 +6500,7 @@ void Parser::ParseTypeQualifierListOpt(
       // recovery is graceful.
       if (AttrReqs & AR_GNUAttributesParsed ||
           AttrReqs & AR_GNUAttributesParsedAndRejected) {
-        ParseGNUAttributes(DS.getAttributes());
+        ParseGNUAttributes(DS.getAttributes(), LateAttrs);
         continue; // do *not* consume the next token!
       }
       // otherwise, FALL THROUGH!


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

Reply via email to