[clang] [BoundsSafety][NFC] Add late-parsed type attribute lists to declarators (PR #224559)
Yeoul Na via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 21 11:46:01 PDT 2026
https://github.com/rapidsna updated https://github.com/llvm/llvm-project/pull/224559
>From 679668421b03bfabb37d0fd412a80dcd8e7ac052 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] Add late-parsed type attribute lists to
declarators
This is groundwork for late-parsing type attributes in every type
position where an ordinary type attribute can appear. 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:
- Move CachedTokens and LateParsedAttrList earlier in DeclSpec.h so
that DeclSpec, Declarator and DeclaratorChunk can hold one. Add
LateParsedAttrList::forTypeAttrs() to create those lists.
- 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. The former passes it to
ParseDeclarationSpecifiers, the latter to ParseGNUAttributes.
No functional change: no caller passes a list yet, so the lists stay
empty. The next commit populates them.
---
clang/include/clang/Parse/Parser.h | 23 +++--
clang/include/clang/Sema/DeclSpec.h | 136 +++++++++++++++++-----------
clang/lib/Parse/ParseDecl.cpp | 14 ++-
3 files changed, 107 insertions(+), 66 deletions(-)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 4e1271da82861..8fff5631c8357 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1957,10 +1957,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
@@ -1971,10 +1973,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
@@ -2681,7 +2685,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 7b58bfcf53d98..77f2bd67a375c 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -214,6 +214,63 @@ 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.
+ ///
+ /// 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) {}
+ };
+
/// Captures information about "declaration specifiers".
///
/// "Declaration specifiers" encompasses storage-class-specifiers,
@@ -408,6 +465,9 @@ class DeclSpec {
// attributes.
ParsedAttributes Attrs;
+ // late attributes
+ LateParsedAttrList LateParsedAttrs;
+
// Scope specifier for the type spec, if applicable.
CXXScopeSpec TypeScope;
@@ -484,7 +544,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 +945,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 +1322,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 +1345,7 @@ struct DeclaratorChunk {
}
ParsedAttributesView AttrList;
+ LateParsedAttrList LateAttrList;
struct PointerTypeInfo {
/// The type qualifiers: const/volatile/restrict/unaligned/atomic.
@@ -2040,6 +2058,8 @@ class Declarator {
/// corresponding constructor parameter.
const ParsedAttributesView &DeclarationAttrs;
+ LateParsedAttrList LateParsedAttrs;
+
/// The asm label, if specified.
Expr *AsmLabel;
@@ -2105,7 +2125,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 +2215,7 @@ class Declarator {
DeclTypeInfo[i].destroy();
DeclTypeInfo.clear();
Attrs.clear();
+ LateParsedAttrs.clear();
AsmLabel = nullptr;
InlineStorageUsed = false;
HasInitializer = false;
@@ -2428,13 +2450,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 +2789,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 ada54c85695c3..b673679a8f56d 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!
More information about the cfe-commits
mailing list