[clang] [BoundsSafety] Support bounds-safety attributes in type positions (PR #179612)
Yeoul Na via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 13 07:33:30 PST 2026
https://github.com/rapidsna updated https://github.com/llvm/llvm-project/pull/179612
>From ecb0c70a30af65db5916027bb6f4508fe0ceadc9 Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Wed, 22 Oct 2025 10:46:46 -0700
Subject: [PATCH 01/10] [BoundsSafety] Support bounds-safety attributes in type
positions
This commit implements proper handling of bounds-safety attributes
(counted_by, counted_by_or_null, sized_by, sized_by_or_null) when they
appear in type attribute positions, correctly treating them as type
attributes rather than declaration attributes.
Previously, these attributes in type positions (e.g., `int * __counted_by(count) buf`)
were incorrectly treated as declaration attributes, leading to:
1. Inability to reference struct members declared later (in late-parse mode)
2. Incorrect acceptance of attributes on nested pointer types
3. Attributes being attached to the wrong type level
This commit ensures that these attributes are correctly added to type positions
even when it's not late parsed. The key changes:
- Introduce LateParsedAttrType placeholder type to defer attribute processing
until the complete struct definition is available (for late-parse mode). The
placeholder is then transformed to a concrete type (e.g., CountAttributedType)
via TreeTransform
- Move LateParsedDeclaration and LateParsedAttribute types to DeclSpec.h out of
Parser, so these types can be used in DeclSpec and Declarator and DeclChunk
(forward declaration did not work because these are nested classes of Parser)
- Introduce LateParsedTypeAttribute which is a child of LateParsedAttribute to
make it clear when late parsed attribute is handled as type attribute
- Add LateParsedAttribute vector to DeclaratorChunk, Declarator, and DeclSpec to
track late type attributes at each declarator level, similar to existing
ParsedAttr handling
- Implement proper attribute distribution to nested type levels during type
construction in GetTypeForDeclarator
- Add more semantic checks to reject attributes on nested pointers
Issue #166411
---
clang/include/clang/AST/ASTContext.h | 7 +
clang/include/clang/AST/RecursiveASTVisitor.h | 6 +
clang/include/clang/AST/TypeBase.h | 41 +++
clang/include/clang/AST/TypeLoc.h | 21 ++
clang/include/clang/AST/TypeProperties.td | 8 +
.../clang/Basic/DiagnosticSemaKinds.td | 3 +
clang/include/clang/Basic/TypeNodes.td | 1 +
clang/include/clang/Parse/Parser.h | 98 ++----
clang/include/clang/Sema/DeclSpec.h | 170 ++++++++-
clang/include/clang/Sema/Sema.h | 5 +-
clang/lib/AST/ASTContext.cpp | 17 +
clang/lib/AST/ASTImporter.cpp | 7 +
clang/lib/AST/ASTStructuralEquivalence.cpp | 7 +
clang/lib/AST/ItaniumMangle.cpp | 1 +
clang/lib/AST/TypeLoc.cpp | 9 +
clang/lib/AST/TypePrinter.cpp | 15 +
clang/lib/CodeGen/CGDebugInfo.cpp | 1 +
clang/lib/CodeGen/CodeGenFunction.cpp | 1 +
clang/lib/Parse/ParseCXXInlineMethods.cpp | 19 +-
clang/lib/Parse/ParseDecl.cpp | 177 ++++++++--
clang/lib/Sema/Sema.cpp | 3 +-
clang/lib/Sema/SemaBoundsSafety.cpp | 80 ++++-
clang/lib/Sema/SemaDecl.cpp | 322 +++++++++++++++++-
clang/lib/Sema/SemaDeclAttr.cpp | 52 +--
clang/lib/Sema/SemaExpr.cpp | 1 +
clang/lib/Sema/SemaType.cpp | 235 ++++++++++++-
clang/lib/Sema/TreeTransform.h | 18 +
clang/lib/Serialization/ASTReader.cpp | 4 +
clang/lib/Serialization/ASTWriter.cpp | 6 +
.../AST/attr-counted-by-or-null-struct-ptrs.c | 23 --
clang/test/AST/attr-counted-by-struct-ptrs.c | 26 --
.../AST/attr-sized-by-or-null-struct-ptrs.c | 24 --
clang/test/AST/attr-sized-by-struct-ptrs.c | 24 --
.../attr-bounds-safety-function-ptr-param.c | 173 ++++++++++
.../attr-counted-by-late-parsed-struct-ptrs.c | 74 ++--
.../Sema/attr-counted-by-or-null-last-field.c | 4 +-
...unted-by-or-null-late-parsed-struct-ptrs.c | 60 +---
...ruct-ptrs-completable-incomplete-pointee.c | 37 +-
.../attr-counted-by-or-null-struct-ptrs.c | 14 +-
...ruct-ptrs-completable-incomplete-pointee.c | 32 +-
clang/test/Sema/attr-counted-by-struct-ptrs.c | 18 +-
.../attr-sized-by-late-parsed-struct-ptrs.c | 50 +--
...sized-by-or-null-late-parsed-struct-ptrs.c | 53 +--
.../Sema/attr-sized-by-or-null-struct-ptrs.c | 14 +-
clang/test/Sema/attr-sized-by-struct-ptrs.c | 12 +-
clang/tools/libclang/CIndex.cpp | 4 +
46 files changed, 1475 insertions(+), 502 deletions(-)
create mode 100644 clang/test/Sema/attr-bounds-safety-function-ptr-param.c
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 68205dd1c1fd9..ac70c99d855c1 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -101,6 +101,7 @@ class CXXConstructorDecl;
class CXXMethodDecl;
class CXXRecordDecl;
class DiagnosticsEngine;
+struct LateParsedTypeAttribute;
class DynTypedNodeList;
class Expr;
enum class FloatModeKind;
@@ -1584,6 +1585,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
bool OrNull,
ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const;
+ /// Return a placeholder type for a late-parsed type attribute.
+ /// This type wraps another type and holds the LateParsedAttribute
+ /// that will be parsed later.
+ QualType getLateParsedAttrType(QualType Wrapped,
+ LateParsedTypeAttribute *LateParsedAttr) const;
+
/// Return the uniqued reference to a type adjusted from the original
/// type to a new type.
QualType getAdjustedType(QualType Orig, QualType New) const;
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index ddec2c52fb681..49e3a870fc10d 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1154,6 +1154,9 @@ DEF_TRAVERSE_TYPE(CountAttributedType, {
TRY_TO(TraverseType(T->desugar()));
})
+DEF_TRAVERSE_TYPE(LateParsedAttrType,
+ { TRY_TO(TraverseType(T->getWrappedType())); })
+
DEF_TRAVERSE_TYPE(BTFTagAttributedType,
{ TRY_TO(TraverseType(T->getWrappedType())); })
@@ -1509,6 +1512,9 @@ DEF_TRAVERSE_TYPELOC(AttributedType,
DEF_TRAVERSE_TYPELOC(CountAttributedType,
{ TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
+DEF_TRAVERSE_TYPELOC(LateParsedAttrType,
+ { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
+
DEF_TRAVERSE_TYPELOC(BTFTagAttributedType,
{ TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index b0fdf178ab3cc..f061ad89f3632 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -70,6 +70,7 @@ class TagDecl;
class TemplateParameterList;
class Type;
class Attr;
+struct LateParsedTypeAttribute;
enum {
TypeAlignmentInBits = 4,
@@ -3484,6 +3485,46 @@ class CountAttributedType final
StringRef getAttributeName(bool WithMacroPrefix) const;
};
+/// Represents a placeholder type for late-parsed type attributes.
+/// This type wraps another type and holds an opaque pointer to a
+/// LateParsedAttribute that will be parsed later (e.g., in ActOnFields).
+/// Once parsed, this type is replaced with the appropriate attributed type
+/// (e.g., CountAttributedType for counted_by).
+class LateParsedAttrType : public Type, public llvm::FoldingSetNode {
+ friend class ASTContext; // ASTContext creates these.
+
+ QualType WrappedTy;
+ LateParsedTypeAttribute *LateParsedTypeAttr;
+
+ LateParsedAttrType(QualType Wrapped, QualType Canon,
+ LateParsedTypeAttribute *Attr)
+ : Type(LateParsedAttr, Canon, Wrapped->getDependence()),
+ WrappedTy(Wrapped), LateParsedTypeAttr(Attr) {}
+
+public:
+ QualType getWrappedType() const { return WrappedTy; }
+ LateParsedTypeAttribute *getLateParsedAttribute() const {
+ return LateParsedTypeAttr;
+ }
+
+ bool isSugared() const { return true; }
+ QualType desugar() const { return WrappedTy; }
+
+ void Profile(llvm::FoldingSetNodeID &ID) {
+ Profile(ID, WrappedTy, LateParsedTypeAttr);
+ }
+
+ static void Profile(llvm::FoldingSetNodeID &ID, QualType Wrapped,
+ LateParsedTypeAttribute *Attr) {
+ ID.AddPointer(Wrapped.getAsOpaquePtr());
+ ID.AddPointer(Attr);
+ }
+
+ static bool classof(const Type *T) {
+ return T->getTypeClass() == LateParsedAttr;
+ }
+};
+
/// Represents a type which was implicitly adjusted by the semantic
/// engine for arbitrary reasons. For example, array and function types can
/// decay, and function types can have their calling conventions adjusted.
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 2cefaa9611c98..85d6937f3bbb9 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -1329,6 +1329,27 @@ class CountAttributedTypeLoc final
SourceRange getLocalSourceRange() const;
};
+struct LateParsedAttrLocInfo {}; // Nothing.
+
+class LateParsedAttrTypeLoc
+ : public ConcreteTypeLoc<UnqualTypeLoc, LateParsedAttrTypeLoc,
+ LateParsedAttrType, LateParsedAttrLocInfo> {
+public:
+ TypeLoc getInnerLoc() const { return getInnerTypeLoc(); }
+
+ void initializeLocal(ASTContext &Context, SourceLocation Loc) {
+ // Nothing to initialize
+ }
+
+ SourceRange getLocalSourceRange() const;
+
+ QualType getInnerType() const { return getTypePtr()->getWrappedType(); }
+
+ LateParsedTypeAttribute *getLateParsedAttribute() const {
+ return getTypePtr()->getLateParsedAttribute();
+ }
+};
+
struct MacroQualifiedLocInfo {
SourceLocation ExpansionLoc;
};
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index b150ac2e1cbe3..854752f0fcb30 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -44,6 +44,14 @@ let Class = CountAttributedType in {
def : Creator<[{ return ctx.getCountAttributedType(WrappedTy, CountExpr, CountInBytes, OrNull, CoupledDecls); }]>;
}
+let Class = LateParsedAttrType in {
+ // Note: LateParsedAttrType is a transient placeholder type that should
+ // normally be replaced before serialization. If serialized, we just
+ // serialize the wrapped type, losing the late-parsed attribute pointer
+ // (which wouldn't be valid after deserialization anyway).
+ def : Creator<[{ (void)ctx; llvm_unreachable("unreachable for serialization"); }]>;
+}
+
let Class = AdjustedType in {
def : Property<"originalType", QualType> {
let Read = [{ node->getOriginalType() }];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4f7934b328d9f..63d6c30f9faca 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7065,6 +7065,9 @@ def err_builtin_counted_by_ref_invalid_use : Error<
"value returned by '__builtin_counted_by_ref' cannot be used in "
"%select{an array subscript|a binary}0 expression">;
+def err_counted_by_on_nested_pointer : Error<
+ "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' attribute on nested pointer type is not allowed">;
+
let CategoryName = "ARC Semantic Issue" in {
// ARC-mode diagnostics.
diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td
index db43a8529f02b..b65fa0d5b786d 100644
--- a/clang/include/clang/Basic/TypeNodes.td
+++ b/clang/include/clang/Basic/TypeNodes.td
@@ -105,6 +105,7 @@ def ObjCInterfaceType : TypeNode<ObjCObjectType>, AlwaysCanonical;
def ObjCObjectPointerType : TypeNode<Type>;
def BoundsAttributedType : TypeNode<Type, 1>;
def CountAttributedType : TypeNode<BoundsAttributedType>, NeverCanonical;
+def LateParsedAttrType : TypeNode<Type>, NeverCanonical;
def PipeType : TypeNode<Type>;
def AtomicType : TypeNode<Type>;
def BitIntType : TypeNode<Type>;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index e03d7994e2fa5..560eeca13623e 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -197,6 +197,8 @@ class Parser : public CodeCompletionHandler {
friend class PoisonSEHIdentifiersRAIIObject;
friend class ParenBraceBracketBalancer;
friend class BalancedDelimiterTracker;
+ friend struct LateParsedAttribute;
+ friend struct LateParsedTypeAttribute;
Parser(Preprocessor &PP, Sema &Actions, bool SkipFunctionBodies);
~Parser() override;
@@ -942,7 +944,6 @@ class Parser : public CodeCompletionHandler {
void SkipFunctionBody();
struct ParsedTemplateInfo;
- class LateParsedAttrList;
/// ParseFunctionDefinition - We parsed and verified that the specified
/// Declarator is well formed. If this is a K&R-style function, read the
@@ -1107,25 +1108,6 @@ class Parser : public CodeCompletionHandler {
private:
struct ParsingClass;
- /// [class.mem]p1: "... the class is regarded as complete within
- /// - function bodies
- /// - default arguments
- /// - exception-specifications (TODO: C++0x)
- /// - and brace-or-equal-initializers for non-static data members
- /// (including such things in nested classes)."
- /// LateParsedDeclarations build the tree of those elements so they can
- /// be parsed after parsing the top-level class.
- class LateParsedDeclaration {
- public:
- virtual ~LateParsedDeclaration();
-
- virtual void ParseLexedMethodDeclarations();
- virtual void ParseLexedMemberInitializers();
- virtual void ParseLexedMethodDefs();
- virtual void ParseLexedAttributes();
- virtual void ParseLexedPragmas();
- };
-
/// Inner node of the LateParsedDeclaration tree that parses
/// all its members recursively.
class LateParsedClass : public LateParsedDeclaration {
@@ -1148,29 +1130,6 @@ class Parser : public CodeCompletionHandler {
ParsingClass *Class;
};
- /// Contains the lexed tokens of an attribute with arguments that
- /// may reference member variables and so need to be parsed at the
- /// end of the class declaration after parsing all other member
- /// member declarations.
- /// FIXME: Perhaps we should change the name of LateParsedDeclaration to
- /// LateParsedTokens.
- struct LateParsedAttribute : public LateParsedDeclaration {
- Parser *Self;
- CachedTokens Toks;
- IdentifierInfo &AttrName;
- IdentifierInfo *MacroII = nullptr;
- SourceLocation AttrNameLoc;
- SmallVector<Decl *, 2> Decls;
-
- explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
- SourceLocation Loc)
- : Self(P), AttrName(Name), AttrNameLoc(Loc) {}
-
- void ParseLexedAttributes() override;
-
- void addDecl(Decl *D) { Decls.push_back(D); }
- };
-
/// Contains the lexed tokens of a pragma with arguments that
/// may reference member variables and so need to be parsed at the
/// end of the class declaration after parsing all other member
@@ -1191,26 +1150,6 @@ class Parser : public CodeCompletionHandler {
void ParseLexedPragmas() override;
};
- // A list of late-parsed attributes. Used by ParseGNUAttributes.
- class LateParsedAttrList : public SmallVector<LateParsedAttribute *, 2> {
- public:
- LateParsedAttrList(bool PSoon = false,
- bool LateAttrParseExperimentalExtOnly = false)
- : ParseSoon(PSoon),
- LateAttrParseExperimentalExtOnly(LateAttrParseExperimentalExtOnly) {}
-
- bool parseSoon() { return ParseSoon; }
- /// returns true iff the attribute to be parsed should only be late parsed
- /// if it is annotated with `LateAttrParseExperimentalExt`
- bool lateAttrParseExperimentalExtOnly() {
- return LateAttrParseExperimentalExtOnly;
- }
-
- private:
- bool ParseSoon; // Are we planning to parse these shortly after creation?
- bool LateAttrParseExperimentalExtOnly;
- };
-
/// Contains the lexed tokens of a member function definition
/// which needs to be parsed at the end of the class declaration
/// after parsing all other member declarations.
@@ -1487,6 +1426,11 @@ class Parser : public CodeCompletionHandler {
void ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
ParsedAttributes *OutAttrs = nullptr);
+
+ void ParseLexedTypeAttribute(LateParsedTypeAttribute &LA, bool EnterScope, ParsedAttributes &OutAttrs);
+
+ static void LateTypeAttrParserCallback(void *P, void *LA, bool EnterScope, ParsedAttributes &OutAttrs);
+
void ParseLexedPragmas(ParsingClass &Class);
void ParseLexedPragma(LateParsedPragma &LP);
@@ -1889,10 +1833,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
@@ -1903,10 +1849,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
@@ -2174,6 +2122,8 @@ class Parser : public CodeCompletionHandler {
ParsedAttributes Attrs(AttrFactory);
ParseGNUAttributes(Attrs, LateAttrs, &D);
D.takeAttributesAppending(Attrs);
+ if (LateAttrs)
+ D.takeLateTypeAttributesAppending(*LateAttrs);
}
}
@@ -2444,7 +2394,8 @@ class Parser : public CodeCompletionHandler {
SourceLocation ScopeLoc,
ParsedAttr::Form Form);
- void DistributeCLateParsedAttrs(Decl *Dcl, LateParsedAttrList *LateAttrs);
+ void DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
+ LateParsedAttrList *LateAttrs);
/// Bounds attributes (e.g., counted_by):
/// \verbatim
@@ -2610,7 +2561,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 43a48c92fc305..a44757664a58d 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -49,6 +49,138 @@ namespace clang {
class Sema;
class Declarator;
struct TemplateIdAnnotation;
+ class Parser;
+
+ /// A set of tokens that has been cached for later parsing.
+ typedef SmallVector<Token, 4> CachedTokens;
+
+ /// [class.mem]p1: "... the class is regarded as complete within
+ /// - function bodies
+ /// - default arguments
+ /// - exception-specifications (TODO: C++0x)
+ /// - and brace-or-equal-initializers for non-static data members
+ /// (including such things in nested classes)."
+ /// LateParsedDeclarations build the tree of those elements so they can
+ /// be parsed after parsing the top-level class.
+ class LateParsedDeclaration {
+ public:
+ virtual ~LateParsedDeclaration();
+
+ virtual void ParseLexedMethodDeclarations();
+ virtual void ParseLexedMemberInitializers();
+ virtual void ParseLexedMethodDefs();
+ virtual void ParseLexedAttributes();
+ virtual void ParseLexedTypeAttributes();
+ virtual void ParseLexedPragmas();
+ };
+
+/// Contains the lexed tokens of an attribute with arguments that
+/// may reference member variables and so need to be parsed at the
+/// end of the class declaration after parsing all other member
+/// member declarations.
+/// FIXME: Perhaps we should change the name of LateParsedDeclaration to
+/// LateParsedTokens.
+struct LateParsedAttribute : public LateParsedDeclaration {
+
+ enum LPA_Kind {
+ LPA_Declaration,
+ LPA_Type,
+ };
+
+ Parser *Self;
+ CachedTokens Toks;
+ IdentifierInfo &AttrName;
+ IdentifierInfo *MacroII = nullptr;
+ SourceLocation AttrNameLoc;
+ SmallVector<Decl *, 2> Decls;
+
+private:
+ LPA_Kind Kind;
+
+public:
+ explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
+ SourceLocation Loc,
+ LPA_Kind Kind = LPA_Declaration)
+ : Self(P), AttrName(Name), AttrNameLoc(Loc), Kind(Kind) {}
+
+ void ParseLexedAttributes() override;
+
+ void addDecl(Decl *D) { Decls.push_back(D); }
+
+ LPA_Kind getKind() const { return Kind; }
+
+ // LLVM-style RTTI support
+ static bool classof(const LateParsedAttribute* LA) {
+ // LateParsedAttribute matches both Declaration and Type kinds
+ return LA->getKind() == LPA_Declaration || LA->getKind() == LPA_Type;
+ }
+};
+
+/// Contains the lexed tokens of an attribute with arguments that
+/// may reference member variables and so need to be parsed at the
+/// end of the class declaration after parsing all other member
+/// member declarations.
+/// FIXME: Perhaps we should change the name of LateParsedDeclaration to
+/// LateParsedTokens.
+struct LateParsedTypeAttribute : public LateParsedAttribute {
+
+ explicit LateParsedTypeAttribute(Parser *P, IdentifierInfo &Name,
+ SourceLocation Loc)
+ : LateParsedAttribute(P, Name, Loc, LPA_Type) {}
+
+ void ParseLexedAttributes() override;
+ void ParseLexedTypeAttributes() override;
+
+ void addDecl(Decl *D) { Decls.push_back(D); }
+
+ /// Parse this late-parsed type attribute and store results in OutAttrs.
+ /// This method can be called from Sema during type transformation to
+ /// parse the cached tokens and produce the final attribute.
+ void ParseInto(ParsedAttributes &OutAttrs);
+
+ // LLVM-style RTTI support
+ static bool classof(const LateParsedAttribute* LA) {
+ return LA->getKind() == LPA_Type;
+ }
+};
+
+// 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() { return ParseSoon; }
+ /// returns true iff the attribute to be parsed should only be late parsed
+ /// if it is annotated with `LateAttrParseExperimentalExt`
+ bool lateAttrParseExperimentalExtOnly() {
+ return LateAttrParseExperimentalExtOnly;
+ }
+
+ bool lateAttrParseTypeAttrOnly() {
+ return LateAttrParseTypeAttrOnly;
+ }
+
+ void takeTypeAttrsAppendingFrom(LateParsedAttrList &Other) {
+ auto it = std::remove_if(Other.begin(), Other.end(), [&](LateParsedAttribute *LA){
+ if (auto *LTA = dyn_cast<LateParsedTypeAttribute>(LA)) {
+ push_back(LTA);
+ return true;
+ }
+ return false;
+ });
+ Other.erase(it, Other.end());
+ }
+
+private:
+ bool ParseSoon; // Are we planning to parse these shortly after creation?
+ bool LateAttrParseExperimentalExtOnly;
+ bool LateAttrParseTypeAttrOnly;
+};
/// Represents a C++ nested-name-specifier or a global scope specifier.
///
@@ -391,6 +523,9 @@ class DeclSpec {
// attributes.
ParsedAttributes Attrs;
+ // late attributes
+ LateParsedAttrList LateParsedAttrs;
+
// Scope specifier for the type spec, if applicable.
CXXScopeSpec TypeScope;
@@ -465,7 +600,7 @@ class DeclSpec {
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; }
@@ -843,10 +978,18 @@ class DeclSpec {
ParsedAttributes &getAttributes() { return Attrs; }
const ParsedAttributes &getAttributes() const { return Attrs; }
+ LateParsedAttrList *getLateAttributePtr() { return &LateParsedAttrs; }
+ LateParsedAttrList &getLateAttributes() { return LateParsedAttrs; }
+ const LateParsedAttrList &getLateAttributes() const { return LateParsedAttrs; }
+
void takeAttributesAppendingingFrom(ParsedAttributes &attrs) {
Attrs.takeAllAppendingFrom(attrs);
}
+ void takeLateTypeAttributesAppendingingFrom(LateParsedAttrList &lateAttrs) {
+ LateParsedAttrs.takeTypeAttrsAppendingFrom(lateAttrs);
+ }
+
/// Finish - This does final analysis of the declspec, issuing diagnostics for
/// things like "_Complex" (lacking an FP type). After calling this method,
/// DeclSpec is guaranteed self-consistent, even if an error occurred.
@@ -1211,15 +1354,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;
-
/// 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
@@ -1237,6 +1377,7 @@ struct DeclaratorChunk {
}
ParsedAttributesView AttrList;
+ LateParsedAttrList LateAttrList;
struct PointerTypeInfo {
/// The type qualifiers: const/volatile/restrict/unaligned/atomic.
@@ -1937,6 +2078,8 @@ class Declarator {
/// corresponding constructor parameter.
const ParsedAttributesView &DeclarationAttrs;
+ LateParsedAttrList LateParsedAttrs;
+
/// The asm label, if specified.
Expr *AsmLabel;
@@ -2002,7 +2145,7 @@ 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(true, true, true), AsmLabel(nullptr),
TrailingRequiresClause(nullptr),
InventedTemplateParameterList(nullptr) {
assert(llvm::all_of(DeclarationAttrs,
@@ -2324,14 +2467,18 @@ class Declarator {
/// EndLoc, which should be the last token of the chunk.
/// 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) {
+ void
+ AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
+ 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
@@ -2661,6 +2808,13 @@ class Declarator {
return DeclarationAttrs;
}
+ LateParsedAttrList &getLateAttributes() { return LateParsedAttrs; }
+ const LateParsedAttrList &getLateAttributes() const { return LateParsedAttrs; }
+
+ void takeLateTypeAttributesAppending(LateParsedAttrList &lateAttrs) {
+ LateParsedAttrs.takeTypeAttrsAppendingFrom(lateAttrs);
+ }
+
/// hasAttributes - do we contain any attributes?
bool hasAttributes() const {
if (!getAttributes().empty() || !getDeclarationAttributes().empty() ||
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index c9ad6860dc625..0bde208c2ac8c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2457,9 +2457,12 @@ class Sema final : public SemaBase {
/// `counted_by_or_null` attribute.
///
/// \returns false iff semantically valid.
- bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
+ bool CheckCountedByAttrOnField(FieldDecl *FD, QualType T, Expr *E, bool CountInBytes,
bool OrNull);
+ bool CheckCountedByAttrOnFieldDecl(FieldDecl *FD, Expr *E, bool CountInBytes,
+ bool OrNull);
+
/// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
/// `__counted_by_or_null` pointer type \param LHSTy.
///
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index f52470a4d7458..07e2d0d3cd943 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -49,6 +49,7 @@
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/Builtins.h"
+#include "clang/Sema/DeclSpec.h"
#include "clang/Basic/CommentOptions.h"
#include "clang/Basic/ExceptionSpecificationType.h"
#include "clang/Basic/IdentifierTable.h"
@@ -2467,6 +2468,9 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
case Type::CountAttributed:
return getTypeInfo(cast<CountAttributedType>(T)->desugar().getTypePtr());
+ case Type::LateParsedAttr:
+ return getTypeInfo(cast<LateParsedAttrType>(T)->getWrappedType().getTypePtr());
+
case Type::BTFTagAttributed:
return getTypeInfo(
cast<BTFTagAttributedType>(T)->getWrappedType().getTypePtr());
@@ -3630,6 +3634,17 @@ QualType ASTContext::getCountAttributedType(
return QualType(CATy, 0);
}
+QualType ASTContext::getLateParsedAttrType(
+ QualType WrappedTy, LateParsedTypeAttribute *LateParsedAttr) const {
+ QualType CanonTy = getCanonicalType(WrappedTy);
+
+ auto *LPATy = new (*this, alignof(LateParsedAttrType))
+ LateParsedAttrType(WrappedTy, CanonTy, LateParsedAttr);
+
+ Types.push_back(LPATy);
+ return QualType(LPATy, 0);
+}
+
QualType
ASTContext::adjustType(QualType Orig,
llvm::function_ref<QualType(QualType)> Adjust) const {
@@ -14587,6 +14602,8 @@ static QualType getCommonSugarTypeNode(const ASTContext &Ctx, const Type *X,
DX->isCountInBytes(), DX->isOrNull(),
CDX);
}
+ case Type::LateParsedAttr:
+ return QualType();
case Type::PredefinedSugar:
assert(cast<PredefinedSugarType>(X)->getKind() !=
cast<PredefinedSugarType>(Y)->getKind());
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 101ab2c40973b..0313f36c6b2a4 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1836,6 +1836,13 @@ ASTNodeImporter::VisitCountAttributedType(const CountAttributedType *T) {
ArrayRef(CoupledDecls));
}
+ExpectedType
+ASTNodeImporter::VisitLateParsedAttrType(const LateParsedAttrType *T) {
+ // LateParsedAttrType is a transient placeholder that should not normally
+ // appear during AST import. Import as the wrapped type.
+ return import(T->getWrappedType());
+}
+
ExpectedType ASTNodeImporter::VisitTemplateTypeParmType(
const TemplateTypeParmType *T) {
Expected<TemplateTypeParmDecl *> ToDeclOrErr = import(T->getDecl());
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index da64c92221837..58a968d1c54a9 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1148,6 +1148,13 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
break;
+ case Type::LateParsedAttr:
+ if (!IsStructurallyEquivalent(Context,
+ cast<LateParsedAttrType>(T1)->getWrappedType(),
+ cast<LateParsedAttrType>(T2)->getWrappedType()))
+ return false;
+ break;
+
case Type::BTFTagAttributed:
if (!IsStructurallyEquivalent(
Context, cast<BTFTagAttributedType>(T1)->getWrappedType(),
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index f54ec7dab8dce..dacc1749a9147 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2426,6 +2426,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
case Type::BitInt:
case Type::DependentBitInt:
case Type::CountAttributed:
+ case Type::LateParsedAttr:
llvm_unreachable("type is illegal as a nested name specifier");
case Type::SubstBuiltinTemplatePack:
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index f54ccf0932bc7..b4b0d45ee1899 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -22,6 +22,7 @@
#include "clang/AST/TypeLocVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
+#include "clang/Sema/DeclSpec.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
@@ -594,6 +595,14 @@ SourceRange CountAttributedTypeLoc::getLocalSourceRange() const {
return getCountExpr() ? getCountExpr()->getSourceRange() : SourceRange();
}
+SourceRange LateParsedAttrTypeLoc::getLocalSourceRange() const {
+ // LateParsedAttrType is a transient type that wraps an unparsed attribute.
+ // Return the attribute's name location if available.
+ if (auto *Attr = getLateParsedAttribute())
+ return SourceRange(Attr->AttrNameLoc);
+ return {};
+}
+
SourceRange BTFTagAttributedTypeLoc::getLocalSourceRange() const {
return getAttr() ? getAttr()->getRange() : SourceRange();
}
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index c7ac9c2a2124f..3ac50f486de56 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -287,6 +287,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,
case Type::SubstTemplateTypeParm:
case Type::MacroQualified:
case Type::CountAttributed:
+ case Type::LateParsedAttr:
CanPrefixQualifiers = false;
break;
@@ -1851,6 +1852,20 @@ void TypePrinter::printCountAttributedAfter(const CountAttributedType *T,
printCountAttributedImpl(T, OS, Policy);
}
+void TypePrinter::printLateParsedAttrBefore(const LateParsedAttrType *T,
+ raw_ostream &OS) {
+ // LateParsedAttrType is a transient placeholder that should not appear
+ // in user-facing output. Just print the wrapped type.
+ printBefore(T->getWrappedType(), OS);
+}
+
+void TypePrinter::printLateParsedAttrAfter(const LateParsedAttrType *T,
+ raw_ostream &OS) {
+ // LateParsedAttrType is a transient placeholder that should not appear
+ // in user-facing output. Just print the wrapped type.
+ printAfter(T->getWrappedType(), OS);
+}
+
void TypePrinter::printAttributedBefore(const AttributedType *T,
raw_ostream &OS) {
// FIXME: Generate this with TableGen.
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 9dcf8ccdec275..2fe979d27be29 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -4218,6 +4218,7 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) {
case Type::Decltype:
case Type::PackIndexing:
case Type::UnaryTransform:
+ case Type::LateParsedAttr:
break;
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 02947c1df2c59..2a2bcb2b29d48 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2596,6 +2596,7 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) {
case Type::SubstTemplateTypeParm:
case Type::MacroQualified:
case Type::CountAttributed:
+ case Type::LateParsedAttr:
// Keep walking after single level desugaring.
type = type.getSingleStepDesugaredType(getContext());
break;
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index bc18881e89110..20b22ec95d342 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -268,12 +268,13 @@ void Parser::ParseCXXNonStaticMemberInitializer(Decl *VarD) {
Toks.push_back(Eof);
}
-Parser::LateParsedDeclaration::~LateParsedDeclaration() {}
-void Parser::LateParsedDeclaration::ParseLexedMethodDeclarations() {}
-void Parser::LateParsedDeclaration::ParseLexedMemberInitializers() {}
-void Parser::LateParsedDeclaration::ParseLexedMethodDefs() {}
-void Parser::LateParsedDeclaration::ParseLexedAttributes() {}
-void Parser::LateParsedDeclaration::ParseLexedPragmas() {}
+LateParsedDeclaration::~LateParsedDeclaration() {}
+void LateParsedDeclaration::ParseLexedMethodDeclarations() {}
+void LateParsedDeclaration::ParseLexedMemberInitializers() {}
+void LateParsedDeclaration::ParseLexedMethodDefs() {}
+void LateParsedDeclaration::ParseLexedAttributes() {}
+void LateParsedDeclaration::ParseLexedTypeAttributes() {}
+void LateParsedDeclaration::ParseLexedPragmas() {}
Parser::LateParsedClass::LateParsedClass(Parser *P, ParsingClass *C)
: Self(P), Class(C) {}
@@ -314,10 +315,14 @@ void Parser::LateParsedMemberInitializer::ParseLexedMemberInitializers() {
Self->ParseLexedMemberInitializer(*this);
}
-void Parser::LateParsedAttribute::ParseLexedAttributes() {
+void LateParsedAttribute::ParseLexedAttributes() {
Self->ParseLexedAttribute(*this, true, false);
}
+void LateParsedTypeAttribute::ParseLexedAttributes() {}
+
+void LateParsedTypeAttribute::ParseLexedTypeAttributes() {}
+
void Parser::LateParsedPragma::ParseLexedPragmas() {
Self->ParseLexedPragma(*this);
}
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 8688ccf41acb5..99ce51895be72 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -107,6 +107,22 @@ static bool IsAttributeLateParsedStandard(const IdentifierInfo &II) {
#undef CLANG_ATTR_LATE_PARSED_LIST
}
+/// returns true iff attribute is annotated with `LateAttrParseExperimentalExt`
+/// in `Attr.td`.
+static bool IsAttributeTypeAttr(ParsedAttr::Kind Kind) {
+ switch (Kind) {
+#define ATTR(NAME)
+#define DECL_OR_TYPE_ATTR(NAME) case ParsedAttr::AT_##NAME:
+#define TYPE_ATTR(NAME) case ParsedAttr::AT_##NAME:
+#include "clang/Basic/AttrList.inc"
+ return true;
+ default: return false;
+#undef DECL_OR_TYPE_ATTR
+#undef TYPE_ATTR
+#undef ATTR
+ }
+}
+
/// Check if the a start and end source location expand to the same macro.
static bool FindLocsWithCommonFileID(Preprocessor &PP, SourceLocation StartLoc,
SourceLocation EndLoc) {
@@ -156,6 +172,10 @@ bool Parser::ParseSingleGNUAttribute(ParsedAttributes &Attrs,
return false;
}
+
+ ParsedAttr::Kind AttrKind =
+ ParsedAttr::getParsedKind(AttrName, nullptr, ParsedAttr::Form::GNU().getSyntax());
+
bool LateParse = false;
if (!LateAttrs)
LateParse = false;
@@ -164,7 +184,8 @@ bool Parser::ParseSingleGNUAttribute(ParsedAttributes &Attrs,
// parsed for `LateAttrParseExperimentalExt` attributes. This will
// only be late parsed if the experimental language option is enabled.
LateParse = getLangOpts().ExperimentalLateParseAttributes &&
- IsAttributeLateParsedExperimentalExt(*AttrName);
+ IsAttributeLateParsedExperimentalExt(*AttrName) &&
+ (IsAttributeTypeAttr(AttrKind) || !LateAttrs->lateAttrParseTypeAttrOnly());
} else {
// The caller did not restrict late parsing to only
// `LateAttrParseExperimentalExt` attributes so late parse
@@ -182,8 +203,13 @@ bool Parser::ParseSingleGNUAttribute(ParsedAttributes &Attrs,
}
// Handle attributes with arguments that require late parsing.
- LateParsedAttribute *LA =
- new LateParsedAttribute(this, *AttrName, AttrNameLoc);
+ // Late parsing for type attributes isn't properly supported in C++ yet.
+ LateParsedAttribute *LA = nullptr;
+ if (IsAttributeTypeAttr(AttrKind) && !getLangOpts().CPlusPlus)
+ LA = new LateParsedTypeAttribute(this, *AttrName, AttrNameLoc);
+ else
+ LA = new LateParsedAttribute(this, *AttrName, AttrNameLoc);
+
LateAttrs->push_back(LA);
// Attributes in a class are parsed at the end of the class, along
@@ -2725,12 +2751,18 @@ 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.
@@ -3136,15 +3168,16 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
}
}
-void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
+void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
LateParsedAttrList *LateAttrs) {
if (!LateAttrs)
return;
+ // Attach `Decl *` to each `LateParsedAttribute *`.
if (Dcl) {
- for (auto *LateAttr : *LateAttrs) {
- if (LateAttr->Decls.empty())
- LateAttr->addDecl(Dcl);
+ for (auto *LA : *LateAttrs) {
+ if (LA->Decls.empty())
+ LA->addDecl(Dcl);
}
}
}
@@ -3217,12 +3250,6 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
ArgExprs.push_back(ArgExpr.get());
Parens.consumeClose();
- ASTContext &Ctx = Actions.getASTContext();
-
- ArgExprs.push_back(IntegerLiteral::Create(
- Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.getSizeType()), 0),
- Ctx.getSizeType(), SourceLocation()));
-
Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, Parens.getCloseLocation()),
AttributeScopeInfo(), ArgExprs.data(), ArgExprs.size(), Form);
}
@@ -3465,6 +3492,10 @@ void Parser::ParseDeclarationSpecifiers(
DS.takeAttributesAppendingingFrom(attrs);
}
+ if (LateAttrs) {
+ DS.takeLateTypeAttributesAppendingingFrom(*LateAttrs);
+ }
+
// If this is not a declaration specifier token, we're done reading decl
// specifiers. First verify that DeclSpec's are consistent.
DS.Finish(Actions, Policy);
@@ -3997,7 +4028,6 @@ void Parser::ParseDeclarationSpecifiers(
case tok::kw___declspec:
ParseAttributes(PAKM_GNU | PAKM_Declspec, DS.getAttributes(), LateAttrs);
continue;
-
// Microsoft single token adornments.
case tok::kw___forceinline: {
isInvalid = DS.setFunctionSpecForceInline(Loc, PrevSpec, DiagID);
@@ -4706,7 +4736,8 @@ void Parser::ParseStructDeclaration(
MaybeParseCXX11Attributes(Attrs);
// Parse the common specifier-qualifiers-list piece.
- ParseSpecifierQualifierList(DS);
+ ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_normal,
+ LateFieldAttrs);
// If there are no declarators, this is a free-standing declaration
// specifier. Let the actions module cope with it.
@@ -4768,7 +4799,7 @@ void Parser::ParseStructDeclaration(
// We're done with this declarator; invoke the callback.
Decl *Field = FieldsCallback(DeclaratorInfo);
if (Field)
- DistributeCLateParsedAttrs(Field, LateFieldAttrs);
+ DistributeCLateParsedAttrs(DeclaratorInfo.D, Field, LateFieldAttrs);
// If we don't have a comma, it is either the end of the list (a ';')
// or an error, bail out.
@@ -4786,12 +4817,64 @@ void Parser::ParseLexedCAttributeList(LateParsedAttrList &LAs, bool EnterScope,
assert(LAs.parseSoon() &&
"Attribute list should be marked for immediate parsing.");
for (auto *LA : LAs) {
+ assert(!isa<LateParsedTypeAttribute>(LA));
ParseLexedCAttribute(*LA, EnterScope, OutAttrs);
delete LA;
}
LAs.clear();
}
+void Parser::ParseLexedTypeAttribute(LateParsedTypeAttribute &LA, bool EnterScope, ParsedAttributes &OutAttrs) {
+ // Create a fake EOF so that attribute parsing won't go off the end of the
+ // attribute.
+ Token AttrEnd;
+ AttrEnd.startToken();
+ AttrEnd.setKind(tok::eof);
+ AttrEnd.setLocation(Tok.getLocation());
+ AttrEnd.setEofData(LA.Toks.data());
+ LA.Toks.push_back(AttrEnd);
+
+ // Append the current token at the end of the new token stream so that it
+ // doesn't get lost.
+ LA.Toks.push_back(Tok);
+ PP.EnterTokenStream(LA.Toks, /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/true);
+ // Drop the current token and bring the first cached one. It's the same token
+ // as when we entered this function.
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
+
+ // Note: EnterScope parameter is not used here. Type attributes are parsed
+ // in the context where ActOnFields is called, which already has the proper
+ // scope established. The actual semantic analysis happens during the
+ // RebuildTypeWithLateParsedAttr transformation, not during token parsing.
+ (void)EnterScope;
+
+ ParsedAttributes Attrs(AttrFactory);
+
+ assert(LA.Decls.size() <= 1 &&
+ "late field attribute expects to have at most one declaration.");
+
+ // Dispatch based on the attribute and parse it
+ ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr, nullptr,
+ SourceLocation(), ParsedAttr::Form::GNU(), nullptr);
+
+ // Due to a parsing error, we either went over the cached tokens or
+ // there are still cached tokens left, so we skip the leftover tokens.
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ // Consume the fake EOF token if it's there
+ if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData())
+ ConsumeAnyToken();
+
+ OutAttrs.takeAllAppendingFrom(Attrs);
+}
+
+void LateParsedTypeAttribute::ParseInto(ParsedAttributes &OutAttrs) {
+ // Delegate to the Parser that created this attribute
+ Self->ParseLexedTypeAttribute(*this, /*EnterScope=*/true, OutAttrs);
+}
+
void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
ParsedAttributes *OutAttrs) {
// Create a fake EOF so that attribute parsing won't go off the end of the
@@ -4812,7 +4895,9 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
// as when we entered this function.
ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
- // TODO: Use `EnterScope`
+ // Note: EnterScope parameter is not currently used because we always use
+ // getCurScope() when calling ActOnFinishDelayedAttribute. This is consistent
+ // with how other late-parsed attributes are handled in struct/class contexts.
(void)EnterScope;
ParsedAttributes Attrs(AttrFactory);
@@ -4841,6 +4926,12 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
}
}
+void Parser::LateTypeAttrParserCallback(void *P, void *OLA, bool EnterScope, ParsedAttributes &OutAttrs) {
+ auto *LA = static_cast<LateParsedAttribute *>(OLA);
+ auto *LTA = cast<LateParsedTypeAttribute>(LA);
+ return ((Parser *)(P))->ParseLexedTypeAttribute(*LTA, /*EnterScope=*/false, OutAttrs);
+}
+
void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
DeclSpec::TST TagType, RecordDecl *TagDecl) {
PrettyDeclStackTraceEntry CrashInfo(Actions.Context, TagDecl, RecordLoc,
@@ -4964,13 +5055,16 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
ParsedAttributes attrs(AttrFactory);
// If attributes exist after struct contents, parse them.
- MaybeParseGNUAttributes(attrs, &LateFieldAttrs);
+ MaybeParseGNUAttributes(attrs);
// Late parse field attributes if necessary.
ParseLexedCAttributeList(LateFieldAttrs, /*EnterScope=*/false);
-
SmallVector<Decl *, 32> FieldDecls(TagDecl->fields());
+ // ActOnFields will transform any LateParsedAttrType placeholders into
+ // proper attributed types. The LateParsedTypeAttribute objects embedded
+ // in those placeholder types already contain everything needed (Parser
+ // pointer and cached tokens) to parse themselves.
Actions.ActOnFields(getCurScope(), RecordLoc, TagDecl, FieldDecls,
T.getOpenLocation(), T.getCloseLocation(), attrs);
StructScope.Exit();
@@ -6129,7 +6223,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);
@@ -6271,7 +6366,9 @@ void Parser::ParseTypeQualifierListOpt(
// recovery is graceful.
if (AttrReqs & AR_GNUAttributesParsed ||
AttrReqs & AR_GNUAttributesParsedAndRejected) {
- ParseGNUAttributes(DS.getAttributes());
+
+ // FIXME: Late parse only when some flag is set.
+ ParseGNUAttributes(DS.getAttributes(), LateAttrs);
continue; // do *not* consume the next token!
}
// otherwise, FALL THROUGH!
@@ -6425,6 +6522,8 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
DeclSpec DS(AttrFactory);
ParseTypeQualifierListOpt(DS);
+ assert(DS.getLateAttributes().empty());
+
D.AddTypeInfo(
DeclaratorChunk::getPipe(DS.getTypeQualifiers(), DS.getPipeLoc()),
std::move(DS.getAttributes()), SourceLocation());
@@ -6452,25 +6551,40 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
((D.getContext() != DeclaratorContext::CXXNew)
? AR_GNUAttributesParsed
: AR_GNUAttributesParsedAndRejected);
+ // FIXME: Don't need to pass parameter. It's not used. This is the path
+ // where it is experimental only.
+ // FIXME: Still don't know whether this is the right context to do late parsing. Is it okay?
+ // You don't want to do late parsing if it's a variable declaration.
+ // You can probably look at the DeclaratorContext!
+ bool LateParsingContext = D.getContext() == DeclaratorContext::Member ||
+ D.getContext() == DeclaratorContext::Prototype;
+ LateParsedAttrList *LateAttrs =
+ LateParsingContext
+ ? &DS.getLateAttributes()
+ : nullptr;
+
ParseTypeQualifierListOpt(DS, Reqs, /*AtomicOrPtrauthAllowed=*/true,
- !D.mayOmitIdentifier());
+ !D.mayOmitIdentifier(), {}, LateAttrs);
D.ExtendWithDeclSpec(DS);
// Recursively parse the declarator.
Actions.runWithSufficientStackSpace(
D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); });
- if (Kind == tok::star)
+ if (Kind == tok::star) {
// Remember that we parsed a pointer type, and remember the type-quals.
D.AddTypeInfo(DeclaratorChunk::getPointer(
DS.getTypeQualifiers(), Loc, DS.getConstSpecLoc(),
DS.getVolatileSpecLoc(), DS.getRestrictSpecLoc(),
DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc()),
- std::move(DS.getAttributes()), SourceLocation());
- else
+ std::move(DS.getAttributes()), SourceLocation(),
+ std::move(DS.getLateAttributes()));
+ } else {
+ assert(DS.getLateAttributes().empty());
// Remember that we parsed a Block type, and remember the type-quals.
D.AddTypeInfo(
DeclaratorChunk::getBlockPointer(DS.getTypeQualifiers(), Loc),
std::move(DS.getAttributes()), SourceLocation());
+ }
} else {
// Is a reference
DeclSpec DS(AttrFactory);
@@ -6523,6 +6637,8 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
}
}
+ assert(DS.getLateAttributes().empty());
+
// Remember that we parsed a reference type.
D.AddTypeInfo(DeclaratorChunk::getReference(DS.getTypeQualifiers(), Loc,
Kind == tok::amp),
@@ -7034,9 +7150,10 @@ void Parser::ParseParenDeclarator(Declarator &D) {
// sort of paren this is.
//
ParsedAttributes attrs(AttrFactory);
+ LateParsedAttrList LateAttrs(true, true, true);
bool RequiresArg = false;
if (Tok.is(tok::kw___attribute)) {
- ParseGNUAttributes(attrs);
+ ParseGNUAttributes(attrs, &LateAttrs);
// We require that the argument list (if this is a non-grouping paren) be
// present even if the attribute list was empty.
@@ -7091,7 +7208,7 @@ void Parser::ParseParenDeclarator(Declarator &D) {
T.consumeClose();
D.AddTypeInfo(
DeclaratorChunk::getParen(T.getOpenLocation(), T.getCloseLocation()),
- std::move(attrs), T.getCloseLocation());
+ std::move(attrs), T.getCloseLocation(), LateAttrs);
D.setGroupingParens(hadGroupingParens);
@@ -7102,6 +7219,8 @@ void Parser::ParseParenDeclarator(Declarator &D) {
return;
}
+ assert(LateAttrs.empty() && "Late parsed type attribute on FirstParamAttr is dropped");
+
// Okay, if this wasn't a grouping paren, it must be the start of a function
// argument list. Recognize that this declarator will never have an
// identifier (and remember where it would have been), then call into
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 6eea8f6e9d97e..7c51711684084 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -276,7 +276,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()),
SourceMgr(PP.getSourceManager()), APINotes(SourceMgr, LangOpts),
AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr),
- LateTemplateParser(nullptr), OpaqueParser(nullptr), CurContext(nullptr),
+ LateTemplateParser(nullptr), OpaqueParser(nullptr),
+ CurContext(nullptr),
ExternalSource(nullptr), StackHandler(Diags), CurScope(nullptr),
Ident_super(nullptr), AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
ARMPtr(std::make_unique<SemaARM>(*this)),
diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp
index de9adf8ef5a1b..7b39ba93b0aac 100644
--- a/clang/lib/Sema/SemaBoundsSafety.cpp
+++ b/clang/lib/Sema/SemaBoundsSafety.cpp
@@ -50,8 +50,8 @@ enum class CountedByInvalidPointeeTypeKind {
VALID,
};
-bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
- bool OrNull) {
+bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, QualType T, Expr *E,
+ bool CountInBytes, bool OrNull) {
// Check the context the attribute is used in
unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
@@ -62,7 +62,7 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
return true;
}
- const auto FieldTy = FD->getType();
+ const auto FieldTy = T;
if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
Diag(FD->getBeginLoc(),
diag::err_count_attr_not_on_ptr_or_flexible_array_member)
@@ -231,6 +231,80 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
return false;
}
+bool Sema::CheckCountedByAttrOnFieldDecl(FieldDecl *FD, Expr *E,
+ bool CountInBytes, bool OrNull) {
+ unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
+
+ if (FD->getParent()->isUnion()) {
+ Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)
+ << Kind << FD->getSourceRange();
+ return true;
+ }
+
+ const QualType FieldTy = FD->getType();
+ LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
+ LangOptions::StrictFlexArraysLevelKind::IncompleteOnly;
+ if (FieldTy->isArrayType() &&
+ !Decl::isFlexibleArrayMemberLike(getASTContext(), FD, FieldTy,
+ StrictFlexArraysLevel, true)) {
+ Diag(FD->getBeginLoc(),
+ diag::err_counted_by_attr_on_array_not_flexible_array_member)
+ << Kind << FD->getLocation();
+ return true;
+ }
+
+ // Validate the expression type
+ if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
+ Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
+ << Kind << E->getSourceRange();
+ return true;
+ }
+
+ auto *DRE = dyn_cast<DeclRefExpr>(E);
+ if (!DRE) {
+ Diag(E->getBeginLoc(),
+ diag::err_count_attr_only_support_simple_decl_reference)
+ << Kind << E->getSourceRange();
+ return true;
+ }
+
+ // Validate count field references
+ auto *CountDecl = DRE->getDecl();
+ FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);
+ if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {
+ CountFD = IFD->getAnonField();
+ }
+ if (!CountFD) {
+ Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
+ << CountDecl << Kind << E->getSourceRange();
+
+ Diag(CountDecl->getBeginLoc(),
+ diag::note_flexible_array_counted_by_attr_field)
+ << CountDecl << CountDecl->getSourceRange();
+ return true;
+ }
+
+ if (FD->getParent() != CountFD->getParent()) {
+ if (CountFD->getParent()->isUnion()) {
+ Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)
+ << Kind << CountFD->getSourceRange();
+ return true;
+ }
+ auto *RD = GetEnclosingNamedOrTopAnonRecord(FD);
+ auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
+
+ if (RD != CountRD) {
+ Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
+ << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
+ Diag(CountFD->getBeginLoc(),
+ diag::note_flexible_array_counted_by_attr_field)
+ << CountFD << CountFD->getSourceRange();
+ return true;
+ }
+ }
+ return false;
+}
+
static void EmitIncompleteCountedByPointeeNotes(Sema &S,
const CountAttributedType *CATy,
NamedDecl *IncompleteTyDecl) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b943ea2f85030..9bd30ecfd7feb 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "TypeLocBuilder.h"
+#include "TreeTransform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTLambda.h"
@@ -16939,7 +16940,7 @@ void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D,
// Always attach attributes to the underlying decl.
if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D))
D = TD->getTemplatedDecl();
- ProcessDeclAttributeList(S, D, Attrs);
+ ProcessDeclAttributeList(S, D, Attrs, ProcessDeclAttributeOptions());
ProcessAPINotes(D);
if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D))
@@ -19662,6 +19663,296 @@ bool Sema::EntirelyFunctionPointers(const RecordDecl *Record) {
return llvm::all_of(Record->decls(), IsFunctionPointerOrForwardDecl);
}
+static QualType handleCountedByAttrField(Sema &S, QualType T, Decl *D, const ParsedAttr &AL) {
+ if (!AL.diagnoseLangOpts(S))
+ return QualType();
+
+ auto *FD = dyn_cast<FieldDecl>(D);
+ assert(FD);
+
+ auto *CountExpr = AL.getArgAsExpr(0);
+ if (!CountExpr)
+ return QualType();
+
+ bool CountInBytes;
+ bool OrNull;
+ switch (AL.getKind()) {
+ case ParsedAttr::AT_CountedBy:
+ CountInBytes = false;
+ OrNull = false;
+ break;
+ case ParsedAttr::AT_CountedByOrNull:
+ CountInBytes = false;
+ OrNull = true;
+ break;
+ case ParsedAttr::AT_SizedBy:
+ CountInBytes = true;
+ OrNull = false;
+ break;
+ case ParsedAttr::AT_SizedByOrNull:
+ CountInBytes = true;
+ OrNull = true;
+ break;
+ default:
+ llvm_unreachable("unexpected counted_by family attribute");
+ }
+
+ return S.BuildCountAttributedArrayOrPointerType(
+ T, CountExpr, CountInBytes, OrNull);
+}
+struct RebuildTypeWithLateParsedAttr
+ : TreeTransform<RebuildTypeWithLateParsedAttr> {
+ FieldDecl *FD;
+
+ RebuildTypeWithLateParsedAttr(Sema &SemaRef, FieldDecl *FD)
+ : TreeTransform(SemaRef), FD(FD) {}
+
+ // Helper to check and diagnose if a type is CountAttributedType
+ bool diagnoseCountAttributedType(QualType Ty, SourceLocation Loc) {
+ if (const auto *CAT = Ty->getAs<CountAttributedType>()) {
+ SemaRef.Diag(Loc, diag::err_counted_by_on_nested_pointer)
+ << CAT->getKind();
+ FD->setInvalidDecl();
+ return true;
+ }
+ return false;
+ }
+
+ QualType TransformLateParsedAttrType(TypeLocBuilder &TLB,
+ LateParsedAttrTypeLoc TL) {
+ const LateParsedAttrType *LPA = TL.getTypePtr();
+ auto *LTA = LPA->getLateParsedAttribute();
+
+ // The LateParsedTypeAttribute contains a pointer to the Parser that
+ // created it, along with the cached tokens. Call ParseInto() to parse
+ // those tokens now and get the resulting attribute.
+ assert(LTA && "LateParsedAttrType must have a LateParsedTypeAttribute");
+
+ AttributeFactory AF{};
+ ParsedAttributes Attrs(AF);
+
+ // Parse the cached attribute tokens
+ LTA->ParseInto(Attrs);
+
+ // Invalid argument
+ if (Attrs.empty())
+ return QualType();
+
+ assert(Attrs.size() == 1);
+ auto &AL = Attrs[0];
+
+ QualType InnerType = TransformType(TLB, TL.getInnerLoc());
+ if (InnerType.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+
+ QualType T = handleCountedByAttrField(SemaRef, InnerType, FD, AL);
+ if (T.isNull()) {
+ AL.setInvalid();
+ FD->setInvalidDecl();
+ return QualType();
+ }
+
+ AL.setUsedAsTypeAttr();
+
+ TLB.push<CountAttributedTypeLoc>(T);
+ return T;
+ }
+
+ QualType TransformPointerType(TypeLocBuilder &TLB, PointerTypeLoc TL) {
+ QualType PointeeType = getDerived().TransformType(TLB, TL.getPointeeLoc());
+ if (PointeeType.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+
+ // Diagnose nested pointer with counted_by attribute
+ // e.g., int * __counted_by(n) *ptr;
+ if (diagnoseCountAttributedType(PointeeType, TL.getSigilLoc()))
+ return QualType();
+
+ QualType Result = TL.getType();
+ if (getDerived().AlwaysRebuild() ||
+ PointeeType != TL.getPointeeLoc().getType()) {
+ Result = getDerived().RebuildPointerType(PointeeType, TL.getSigilLoc());
+ if (Result.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+ }
+
+ PointerTypeLoc NewTL = TLB.push<PointerTypeLoc>(Result);
+ NewTL.setSigilLoc(TL.getSigilLoc());
+ return Result;
+ }
+
+ QualType TransformConstantArrayType(TypeLocBuilder &TLB,
+ ConstantArrayTypeLoc TL) {
+ const ConstantArrayType *T = TL.getTypePtr();
+ QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());
+ if (ElementType.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+
+ // Diagnose array with element type having counted_by attribute
+ // e.g., int * __counted_by(n) arr[10];
+ if (diagnoseCountAttributedType(ElementType, TL.getLBracketLoc()))
+ return QualType();
+
+ // Continue with normal array transformation
+ Expr *OldSize = TL.getSizeExpr();
+ if (!OldSize)
+ OldSize = const_cast<Expr *>(T->getSizeExpr());
+ Expr *NewSize = nullptr;
+ if (OldSize) {
+ EnterExpressionEvaluationContext Unevaluated(
+ SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ NewSize = getDerived().TransformExpr(OldSize).template getAs<Expr>();
+ NewSize = SemaRef.ActOnConstantExpression(NewSize).get();
+ }
+
+ QualType Result = TL.getType();
+ if (getDerived().AlwaysRebuild() || ElementType != T->getElementType() ||
+ (T->getSizeExpr() && NewSize != OldSize)) {
+ Result = getDerived().RebuildConstantArrayType(
+ ElementType, T->getSizeModifier(), T->getSize(), NewSize,
+ T->getIndexTypeCVRQualifiers(), TL.getBracketsRange());
+ if (Result.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+ }
+
+ ArrayTypeLoc NewTL = TLB.push<ArrayTypeLoc>(Result);
+ NewTL.setLBracketLoc(TL.getLBracketLoc());
+ NewTL.setRBracketLoc(TL.getRBracketLoc());
+ NewTL.setSizeExpr(NewSize);
+
+ return Result;
+ }
+
+ QualType TransformIncompleteArrayType(TypeLocBuilder &TLB,
+ IncompleteArrayTypeLoc TL) {
+ const IncompleteArrayType *T = TL.getTypePtr();
+ QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());
+ if (ElementType.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+
+ // Diagnose flexible array member with element type having counted_by attribute
+ // e.g., int * __counted_by(n) arr[];
+ if (diagnoseCountAttributedType(ElementType, TL.getLBracketLoc()))
+ return QualType();
+
+ QualType Result = TL.getType();
+ if (getDerived().AlwaysRebuild() || ElementType != T->getElementType()) {
+ Result = getDerived().RebuildIncompleteArrayType(
+ ElementType, T->getSizeModifier(), T->getIndexTypeCVRQualifiers(),
+ TL.getBracketsRange());
+ if (Result.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+ }
+
+ IncompleteArrayTypeLoc NewTL = TLB.push<IncompleteArrayTypeLoc>(Result);
+ NewTL.setLBracketLoc(TL.getLBracketLoc());
+ NewTL.setRBracketLoc(TL.getRBracketLoc());
+ NewTL.setSizeExpr(nullptr);
+
+ return Result;
+ }
+
+ QualType TransformVariableArrayType(TypeLocBuilder &TLB,
+ VariableArrayTypeLoc TL) {
+ const VariableArrayType *T = TL.getTypePtr();
+ QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());
+ if (ElementType.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+
+ // Diagnose VLA with element type having counted_by attribute
+ // e.g., int * __counted_by(n) arr[m];
+ if (diagnoseCountAttributedType(ElementType, TL.getLBracketLoc()))
+ return QualType();
+
+ // Transform the size expression
+ ExprResult SizeResult = getDerived().TransformExpr(T->getSizeExpr());
+ if (SizeResult.isInvalid()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+ Expr *Size = SizeResult.get();
+
+ QualType Result = TL.getType();
+ if (getDerived().AlwaysRebuild() || ElementType != T->getElementType() ||
+ Size != T->getSizeExpr()) {
+ Result = getDerived().RebuildVariableArrayType(
+ ElementType, T->getSizeModifier(), Size,
+ T->getIndexTypeCVRQualifiers(), TL.getBracketsRange());
+ if (Result.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+ }
+
+ VariableArrayTypeLoc NewTL = TLB.push<VariableArrayTypeLoc>(Result);
+ NewTL.setLBracketLoc(TL.getLBracketLoc());
+ NewTL.setRBracketLoc(TL.getRBracketLoc());
+ NewTL.setSizeExpr(Size);
+
+ return Result;
+ }
+
+ QualType TransformDependentSizedArrayType(TypeLocBuilder &TLB,
+ DependentSizedArrayTypeLoc TL) {
+ const DependentSizedArrayType *T = TL.getTypePtr();
+ QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());
+ if (ElementType.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+
+ // Diagnose dependent-sized array with element type having counted_by attribute
+ // e.g., template<int N> struct S { int * __counted_by(n) arr[N]; };
+ if (diagnoseCountAttributedType(ElementType, TL.getLBracketLoc()))
+ return QualType();
+
+ // Transform the size expression
+ EnterExpressionEvaluationContext Unevaluated(
+ SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
+ ExprResult SizeResult = getDerived().TransformExpr(T->getSizeExpr());
+ if (SizeResult.isInvalid()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+ Expr *Size = SizeResult.get();
+
+ QualType Result = TL.getType();
+ if (getDerived().AlwaysRebuild() || ElementType != T->getElementType() ||
+ Size != T->getSizeExpr()) {
+ Result = getDerived().RebuildDependentSizedArrayType(
+ ElementType, T->getSizeModifier(), Size,
+ T->getIndexTypeCVRQualifiers(), TL.getBracketsRange());
+ if (Result.isNull()) {
+ FD->setInvalidDecl();
+ return QualType();
+ }
+ }
+
+ DependentSizedArrayTypeLoc NewTL = TLB.push<DependentSizedArrayTypeLoc>(Result);
+ NewTL.setLBracketLoc(TL.getLBracketLoc());
+ NewTL.setRBracketLoc(TL.getRBracketLoc());
+ NewTL.setSizeExpr(Size);
+
+ return Result;
+ }
+};
+
void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
ArrayRef<Decl *> Fields, SourceLocation LBrac,
SourceLocation RBrac,
@@ -19699,6 +19990,35 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
}
}
+ // Transform types with late-parsed type attributes.
+ // Late-parsed type attributes are stored as placeholder LateParsedAttrType
+ // nodes. We need to transform them into proper attributed types now that
+ // all fields are visible and can be referenced.
+ // This is only enabled when -fexperimental-late-parse-attributes is set.
+ if (getLangOpts().ExperimentalLateParseAttributes) {
+ for (ArrayRef<Decl *>::iterator i = Fields.begin(), end = Fields.end();
+ i != end; ++i) {
+ FieldDecl *FD = cast<FieldDecl>(*i);
+
+ RebuildTypeWithLateParsedAttr RebuildFieldType(*this, FD);
+ auto *TSI = RebuildFieldType.TransformType(FD->getTypeSourceInfo());
+ if (TSI) {
+ FD->setTypeSourceInfo(TSI);
+ FD->setType(TSI->getType());
+ }
+ }
+ }
+
+ // Perform FieldDecl-dependent validation for counted_by family attributes
+ for (ArrayRef<Decl *>::iterator i = Fields.begin(), end = Fields.end();
+ i != end; ++i) {
+ FieldDecl *FD = cast<FieldDecl>(*i);
+ if (auto *CAT = FD->getType()->getAs<CountAttributedType>()) {
+ CheckCountedByAttrOnFieldDecl(FD, CAT->getCountExpr(),
+ CAT->isCountInBytes(), CAT->isOrNull());
+ }
+ }
+
// Verify that all the fields are okay.
SmallVector<FieldDecl*, 32> RecFields;
const FieldDecl *PreviousField = nullptr;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 9b6046cdf1ceb..ebe877d59a55e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6640,45 +6640,6 @@ static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(ZeroCallUsedRegsAttr::Create(S.Context, Kind, AL));
}
-static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
- auto *FD = dyn_cast<FieldDecl>(D);
- assert(FD);
-
- auto *CountExpr = AL.getArgAsExpr(0);
- if (!CountExpr)
- return;
-
- bool CountInBytes;
- bool OrNull;
- switch (AL.getKind()) {
- case ParsedAttr::AT_CountedBy:
- CountInBytes = false;
- OrNull = false;
- break;
- case ParsedAttr::AT_CountedByOrNull:
- CountInBytes = false;
- OrNull = true;
- break;
- case ParsedAttr::AT_SizedBy:
- CountInBytes = true;
- OrNull = false;
- break;
- case ParsedAttr::AT_SizedByOrNull:
- CountInBytes = true;
- OrNull = true;
- break;
- default:
- llvm_unreachable("unexpected counted_by family attribute");
- }
-
- if (S.CheckCountedByAttrOnField(FD, CountExpr, CountInBytes, OrNull))
- return;
-
- QualType CAT = S.BuildCountAttributedArrayOrPointerType(
- FD->getType(), CountExpr, CountInBytes, OrNull);
- FD->setType(CAT);
-}
-
static void handleFunctionReturnThunksAttr(Sema &S, Decl *D,
const ParsedAttr &AL) {
StringRef KindStr;
@@ -7735,13 +7696,6 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
handleAvailableOnlyInDefaultEvalMethod(S, D, AL);
break;
- case ParsedAttr::AT_CountedBy:
- case ParsedAttr::AT_CountedByOrNull:
- case ParsedAttr::AT_SizedBy:
- case ParsedAttr::AT_SizedByOrNull:
- handleCountedByAttrField(S, D, AL);
- break;
-
// Microsoft attributes:
case ParsedAttr::AT_LayoutVersion:
handleLayoutVersion(S, D, AL);
@@ -8034,9 +7988,9 @@ static bool isKernelDecl(Decl *D) {
D->hasAttr<CUDAGlobalAttr>();
}
-void Sema::ProcessDeclAttributeList(
- Scope *S, Decl *D, const ParsedAttributesView &AttrList,
- const ProcessDeclAttributeOptions &Options) {
+void Sema::ProcessDeclAttributeList(Scope *S, Decl *D,
+ const ParsedAttributesView &AttrList,
+ const ProcessDeclAttributeOptions &Options) {
if (AttrList.empty())
return;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e12e4b204afad..0c7fcc54aebd9 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4555,6 +4555,7 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T,
case Type::SubstTemplateTypeParm:
case Type::MacroQualified:
case Type::CountAttributed:
+ case Type::LateParsedAttr:
// Keep walking after single level desugaring.
T = T.getSingleStepDesugaredType(Context);
break;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 7ef83433326ed..d0eb7eae44818 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -398,6 +398,8 @@ processTypeAttrs(TypeProcessingState &state, QualType &type,
TypeAttrLocation TAL, const ParsedAttributesView &attrs,
CUDAFunctionTarget CFT = CUDAFunctionTarget::HostDevice);
+static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type, const LateParsedAttrList &LateAttrs, unsigned chunkIndex = 0);
+
static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
QualType &type, CUDAFunctionTarget CFT);
@@ -1485,6 +1487,9 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
// are never distributed.
processTypeAttrs(state, Result, TAL_DeclSpec, SlidingAttrs);
processTypeAttrs(state, Result, TAL_DeclSpec, DS.getAttributes());
+
+ // Process the late attributes that appeared after the type name
+ processLateTypeAttrs(state, Result, DS.getLateAttributes());
}
// Apply const/volatile/restrict qualifiers to T.
@@ -5424,10 +5429,14 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
AreDeclaratorChunksValid = false;
}
+ // TODO: Check the nested level for bounds attributes inside.
// See if there are any attributes on this declarator chunk.
processTypeAttrs(state, T, TAL_DeclChunk, DeclType.getAttrs(),
S.CUDA().IdentifyTarget(D.getAttributes()));
+ // TODO: Check the nested level here.
+ processLateTypeAttrs(state, T, DeclType.LateAttrList, chunkIndex);
+
if (DeclType.Kind != DeclaratorChunk::Paren) {
if (ExpectNoDerefChunk && !IsNoDerefableChunk(DeclType))
S.Diag(DeclType.Loc, diag::warn_noderef_on_non_pointer_or_array);
@@ -5607,6 +5616,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
processTypeAttrs(state, T, TAL_DeclName, NonSlidingAttrs);
processTypeAttrs(state, T, TAL_DeclName, D.getAttributes());
+ processLateTypeAttrs(state, T, D.getLateAttributes());
+
// Diagnose any ignored type attributes.
state.diagnoseIgnoredTypeAttrs(T);
@@ -6195,6 +6206,9 @@ namespace {
void VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) {
// nothing
}
+ void VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
+ // nothing
+ }
void VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// nothing
}
@@ -6359,6 +6373,8 @@ GetTypeSourceInfoForDeclarator(TypeProcessingState &State,
break;
}
+ case TypeLoc::CountAttributed:
+ case TypeLoc::LateParsedAttr:
case TypeLoc::Adjusted:
case TypeLoc::BTFTagAttributed: {
CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc();
@@ -8825,6 +8841,213 @@ static void HandleHLSLParamModifierAttr(TypeProcessingState &State,
}
}
+static CountAttributedType::DynamicCountPointerKind
+getCountAttrKind(bool CountInBytes, bool OrNull) {
+ if (CountInBytes)
+ return OrNull ? CountAttributedType::SizedByOrNull
+ : CountAttributedType::SizedBy;
+ return OrNull ? CountAttributedType::CountedByOrNull
+ : CountAttributedType::CountedBy;
+}
+
+enum class CountedByInvalidPointeeTypeKind {
+ INCOMPLETE,
+ SIZELESS,
+ FUNCTION,
+ FLEXIBLE_ARRAY_MEMBER,
+ VALID,
+};
+
+/// Calculate the pointer nesting level for counted_by attribute validation.
+/// Counts the number of pointer/array/function declarator chunks before the
+/// specified chunk index.
+/// \param state The type processing state
+/// \param chunkIndex The index of the current declarator chunk
+/// \return The number of pointer/array/function chunks before chunkIndex
+static unsigned getPointerNestLevel(TypeProcessingState &state,
+ unsigned chunkIndex) {
+ unsigned pointerNestLevel = 0;
+ if (chunkIndex > 0) {
+ const auto &stateDeclarator = state.getDeclarator();
+ assert(chunkIndex <= stateDeclarator.getNumTypeObjects());
+ // DeclChunks are ordered identifier out. Index 0 is the outer most type object.
+ // Find outer pointer, array or function.
+ for (unsigned i = 0; i < chunkIndex; ++i) {
+ auto TypeObject = stateDeclarator.getTypeObject(i);
+ switch (TypeObject.Kind) {
+ case DeclaratorChunk::Function:
+ case DeclaratorChunk::Array:
+ case DeclaratorChunk::Pointer:
+ pointerNestLevel++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return pointerNestLevel;
+}
+
+static bool validateCountedByAttrType(Sema &S, QualType Ty,
+ ParsedAttr::Kind AttrKind,
+ SourceLocation AttrLoc,
+ unsigned pointerNestLevel,
+ bool &CountInBytes, bool &OrNull) {
+ switch (AttrKind) {
+ case ParsedAttr::AT_CountedBy:
+ CountInBytes = false;
+ OrNull = false;
+ break;
+ case ParsedAttr::AT_CountedByOrNull:
+ CountInBytes = false;
+ OrNull = true;
+ break;
+ case ParsedAttr::AT_SizedBy:
+ CountInBytes = true;
+ OrNull = false;
+ break;
+ case ParsedAttr::AT_SizedByOrNull:
+ CountInBytes = true;
+ OrNull = true;
+ break;
+ default:
+ llvm_unreachable("unexpected counted_by family attribute");
+ }
+
+ unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
+
+ if (Ty->isArrayType() && (CountInBytes || OrNull)) {
+ S.Diag(AttrLoc, diag::err_count_attr_not_on_ptr_or_flexible_array_member)
+ << Kind << /* suggest counted_by */ 1;
+ return false;
+ }
+ if (!Ty->isArrayType() && !Ty->isPointerType()) {
+ S.Diag(AttrLoc, diag::err_count_attr_not_on_ptr_or_flexible_array_member)
+ << Kind << /* do not suggest counted_by */ 0;
+ return false;
+ }
+
+ // Validate pointee type
+ CountedByInvalidPointeeTypeKind InvalidTypeKind =
+ CountedByInvalidPointeeTypeKind::VALID;
+ QualType PointeeTy;
+ int SelectPtrOrArr = 0;
+ if (Ty->isPointerType()) {
+ PointeeTy = Ty->getPointeeType();
+ SelectPtrOrArr = 0;
+ } else {
+ assert(Ty->isArrayType());
+ const ArrayType *AT = S.getASTContext().getAsArrayType(Ty);
+ PointeeTy = AT->getElementType();
+ SelectPtrOrArr = 1;
+ }
+
+ bool ShouldWarn = false;
+ if (PointeeTy->isAlwaysIncompleteType() && !CountInBytes) {
+ bool IsVoidPtr = PointeeTy->isVoidType();
+ if (IsVoidPtr) {
+ S.Diag(AttrLoc, diag::ext_gnu_counted_by_void_ptr) << Kind;
+ S.Diag(AttrLoc, diag::note_gnu_counted_by_void_ptr_use_sized_by) << Kind;
+ assert(InvalidTypeKind == CountedByInvalidPointeeTypeKind::VALID);
+ } else {
+ InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
+ }
+ } else if (PointeeTy->isSizelessType()) {
+ InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
+ } else if (PointeeTy->isFunctionType()) {
+ InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;
+ } else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {
+ if (Ty->isArrayType() && !S.getLangOpts().BoundsSafety) {
+ ShouldWarn = true;
+ }
+ InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
+ }
+
+ if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
+ unsigned DiagID = ShouldWarn
+ ? diag::warn_counted_by_attr_elt_type_unknown_size
+ : diag::err_counted_by_attr_pointee_unknown_size;
+ S.Diag(AttrLoc, DiagID)
+ << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
+ << (ShouldWarn ? 1 : 0) << Kind;
+ return false;
+ }
+
+ if (pointerNestLevel > 0) {
+ S.Diag(AttrLoc, diag::err_counted_by_on_nested_pointer)
+ << Kind;
+ return false;
+ }
+
+ return true;
+}
+
+static void HandleCountedByAttrOnType(TypeProcessingState &State,
+ QualType &CurType, ParsedAttr &Attr) {
+ Sema &S = State.getSema();
+
+ // This attribute is only supported in C.
+ // FIXME: we should implement checkCommonAttributeFeatures() in SemaAttr.cpp
+ // such that it handles type attributes, and then call that from
+ // processTypeAttrs() instead of one-off checks like this.
+ if (!Attr.diagnoseLangOpts(S)) {
+ Attr.setInvalid();
+ return;
+ }
+
+ auto *CountExpr = Attr.getArgAsExpr(0);
+ if (!CountExpr)
+ return;
+
+ // This is a mechanism to prevent nested count pointer types in the contexts
+ // where late parsing isn't allowed: currently that is any context other than
+ // struct fields. In the context where late parsing is allowed, the level check
+ // will be done once the whole context is constructed.
+ unsigned chunkIndex = State.getCurrentChunkIndex();
+ unsigned pointerNestLevel = 0;
+
+ // Only calculate pointer nest level if we're processing a declarator chunk.
+ // For DeclSpec attributes, the declarator hasn't been constructed yet.
+ if (chunkIndex > 0) {
+ pointerNestLevel = getPointerNestLevel(State, chunkIndex);
+ }
+
+ bool CountInBytes, OrNull;
+ if (!validateCountedByAttrType(S, CurType, Attr.getKind(), Attr.getLoc(),
+ pointerNestLevel, CountInBytes, OrNull)) {
+ Attr.setInvalid();
+ return;
+ }
+
+ CurType = S.BuildCountAttributedArrayOrPointerType(CurType, CountExpr,
+ CountInBytes, OrNull);
+}
+
+static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type, const LateParsedAttrList &LateAttrs, unsigned chunkIndex) {
+
+ if (LateAttrs.empty())
+ return true;
+
+ Sema &S = state.getSema();
+
+ // For late type attributes, the check will be done separately.
+ unsigned pointerNestLevel = 0;
+
+ for (auto *LA : LateAttrs) {
+ ParsedAttr::Kind AttrKind =
+ ParsedAttr::getParsedKind(&LA->AttrName, nullptr, ParsedAttr::Form::GNU().getSyntax());
+
+ bool CountInBytes, OrNull;
+ if (!validateCountedByAttrType(S, type, AttrKind, LA->AttrNameLoc,
+ pointerNestLevel, CountInBytes, OrNull))
+ return false;
+
+ type = S.getASTContext().getLateParsedAttrType(
+ type, static_cast<LateParsedTypeAttribute *>(LA));
+ }
+ return true;
+}
+
static void processTypeAttrs(TypeProcessingState &state, QualType &type,
TypeAttrLocation TAL,
const ParsedAttributesView &attrs,
@@ -9019,6 +9242,14 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
break;
}
+ case ParsedAttr::AT_CountedBy:
+ case ParsedAttr::AT_CountedByOrNull:
+ case ParsedAttr::AT_SizedBy:
+ case ParsedAttr::AT_SizedByOrNull:
+ HandleCountedByAttrOnType(state, type, attr);
+ attr.setUsedAsTypeAttr();
+ break;
+
MS_TYPE_ATTRS_CASELIST:
if (!handleMSPointerTypeQualifierAttr(state, attr, type))
attr.setUsedAsTypeAttr();
@@ -9726,7 +9957,9 @@ QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
Expr *CountExpr,
bool CountInBytes,
bool OrNull) {
- assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType());
+ // Accept any array or pointer type here. For arrays, validation that it's
+ // a flexible array member is deferred until CheckCountedByAttrOnFieldDecl.
+ assert(WrappedTy->isArrayType() || WrappedTy->isPointerType());
llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
BuildTypeCoupledDecls(CountExpr, Decls);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index bc923c80b7132..2df62b34834fa 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7730,6 +7730,24 @@ QualType TreeTransform<Derived>::TransformCountAttributedType(
return Result;
}
+template <typename Derived>
+QualType TreeTransform<Derived>::TransformLateParsedAttrType(
+ TypeLocBuilder &TLB, LateParsedAttrTypeLoc TL) {
+ const LateParsedAttrType *OldTy = TL.getTypePtr();
+ QualType InnerTy = getDerived().TransformType(TLB, TL.getInnerLoc());
+ if (InnerTy.isNull())
+ return QualType();
+
+ QualType Result = TL.getType();
+ if (getDerived().AlwaysRebuild() || InnerTy != OldTy->getWrappedType()) {
+ Result = SemaRef.Context.getLateParsedAttrType(
+ InnerTy, OldTy->getLateParsedAttribute());
+ }
+
+ TLB.push<LateParsedAttrTypeLoc>(Result);
+ return Result;
+}
+
template <typename Derived>
QualType TreeTransform<Derived>::TransformBTFTagAttributedType(
TypeLocBuilder &TLB, BTFTagAttributedTypeLoc TL) {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index e518c5cca32bb..e21a5122c4390 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7529,6 +7529,10 @@ void TypeLocReader::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) {
// Nothing to do
}
+void TypeLocReader::VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
+ // Nothing to do
+}
+
void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index f9176b7e68f73..388b1f3924ec4 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -156,6 +156,8 @@ static TypeCode getTypeCodeForTypeClass(Type::TypeClass id) {
#define TYPE_BIT_CODE(CLASS_ID, CODE_ID, CODE_VALUE) \
case Type::CLASS_ID: return TYPE_##CODE_ID;
#include "clang/Serialization/TypeBitCodes.def"
+ case Type::LateParsedAttr:
+ llvm_unreachable("should be replaced with a concrete type before serialization");
case Type::Builtin:
llvm_unreachable("shouldn't be serializing a builtin type this way");
}
@@ -600,6 +602,10 @@ void TypeLocWriter::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) {
// Nothing to do
}
+void TypeLocWriter::VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
+ // Nothing to do
+}
+
void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
diff --git a/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c b/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
index d42547003f0b3..ca603402f0a34 100644
--- a/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
+++ b/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
@@ -56,29 +56,6 @@ struct on_member_pointer_complete_ty_ty_pos {
struct size_known *__counted_by_or_null(count) buf;
};
-// TODO: This should be forbidden but isn't due to counted_by_or_null being treated as a
-// declaration attribute. The attribute ends up on the outer most pointer
-// (allowed by sema) even though syntactically its supposed to be on the inner
-// pointer (would not allowed by sema due to pointee being a function type).
-// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_fn_ptr_ty_ty_pos_inner definition
-// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT: `-FieldDecl {{.+}} fn_ptr 'void (** __counted_by_or_null(count))(void)':'void (**)(void)'
-struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
- int count;
- void (* __counted_by_or_null(count) * fn_ptr)(void);
-};
-
-// FIXME: The generated AST here is wrong. The attribute should be on the inner
-// pointer.
-// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_inner definition
-// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known ** __counted_by_or_null(count)':'struct size_known **'
-struct on_nested_pointer_inner {
- int count;
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__counted_by_or_null` can only be nested when used in function parameters.
- struct size_known *__counted_by_or_null(count) *buf;
-};
// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_outer definition
// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
diff --git a/clang/test/AST/attr-counted-by-struct-ptrs.c b/clang/test/AST/attr-counted-by-struct-ptrs.c
index afef9c8c3b95d..414a7007c7b49 100644
--- a/clang/test/AST/attr-counted-by-struct-ptrs.c
+++ b/clang/test/AST/attr-counted-by-struct-ptrs.c
@@ -45,8 +45,6 @@ struct on_pointer_anon_count {
//==============================================================================
// __counted_by on struct member pointer in type attribute position
//==============================================================================
-// TODO: Correctly parse counted_by as a type attribute. Currently it is parsed
-// as a declaration attribute
// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_complete_ty_ty_pos definition
// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
@@ -56,30 +54,6 @@ struct on_member_pointer_complete_ty_ty_pos {
struct size_known *__counted_by(count) buf;
};
-// TODO: This should be forbidden but isn't due to counted_by being treated as a
-// declaration attribute. The attribute ends up on the outer most pointer
-// (allowed by sema) even though syntactically its supposed to be on the inner
-// pointer (would not allowed by sema due to pointee being a function type).
-// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_fn_ptr_ty_ty_pos_inner definition
-// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT: `-FieldDecl {{.+}} fn_ptr 'void (** __counted_by(count))(void)':'void (**)(void)'
-struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
- int count;
- void (* __counted_by(count) * fn_ptr)(void);
-};
-
-// FIXME: The generated AST here is wrong. The attribute should be on the inner
-// pointer.
-// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_inner definition
-// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known ** __counted_by(count)':'struct size_known **'
-struct on_nested_pointer_inner {
- int count;
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__counted_by` can only be nested when used in function parameters.
- struct size_known *__counted_by(count) *buf;
-};
-
// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_outer definition
// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known ** __counted_by(count)':'struct size_known **'
diff --git a/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c b/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
index 7273280e4b60c..2592d0fd5cb4e 100644
--- a/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
+++ b/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
@@ -56,30 +56,6 @@ struct on_member_pointer_complete_ty_ty_pos {
struct size_known *__sized_by_or_null(count) buf;
};
-// TODO: This should be forbidden but isn't due to sized_by_or_null being treated as a
-// declaration attribute. The attribute ends up on the outer most pointer
-// (allowed by sema) even though syntactically its supposed to be on the inner
-// pointer (would not allowed by sema due to pointee being a function type).
-// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_fn_ptr_ty_ty_pos_inner definition
-// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT: `-FieldDecl {{.+}} fn_ptr 'void (** __sized_by_or_null(count))(void)':'void (**)(void)'
-struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
- int count;
- void (* __sized_by_or_null(count) * fn_ptr)(void);
-};
-
-// FIXME: The generated AST here is wrong. The attribute should be on the inner
-// pointer.
-// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_inner definition
-// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known ** __sized_by_or_null(count)':'struct size_known **'
-struct on_nested_pointer_inner {
- int count;
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__sized_by_or_null` can only be nested when used in function parameters.
- struct size_known *__sized_by_or_null(count) *buf;
-};
-
// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_outer definition
// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known ** __sized_by_or_null(count)':'struct size_known **'
diff --git a/clang/test/AST/attr-sized-by-struct-ptrs.c b/clang/test/AST/attr-sized-by-struct-ptrs.c
index 738eaf8cbf36b..4d7797fa82395 100644
--- a/clang/test/AST/attr-sized-by-struct-ptrs.c
+++ b/clang/test/AST/attr-sized-by-struct-ptrs.c
@@ -56,30 +56,6 @@ struct on_member_pointer_complete_ty_ty_pos {
struct size_known *__sized_by(count) buf;
};
-// TODO: This should be forbidden but isn't due to sized_by being treated as a
-// declaration attribute. The attribute ends up on the outer most pointer
-// (allowed by sema) even though syntactically its supposed to be on the inner
-// pointer (would not allowed by sema due to pointee being a function type).
-// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_fn_ptr_ty_ty_pos_inner definition
-// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT: `-FieldDecl {{.+}} fn_ptr 'void (** __sized_by(count))(void)':'void (**)(void)'
-struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
- int count;
- void (* __sized_by(count) * fn_ptr)(void);
-};
-
-// FIXME: The generated AST here is wrong. The attribute should be on the inner
-// pointer.
-// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_inner definition
-// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known ** __sized_by(count)':'struct size_known **'
-struct on_nested_pointer_inner {
- int count;
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__sized_by` can only be nested when used in function parameters.
- struct size_known *__sized_by(count) *buf;
-};
-
// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_outer definition
// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int'
// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known ** __sized_by(count)':'struct size_known **'
diff --git a/clang/test/Sema/attr-bounds-safety-function-ptr-param.c b/clang/test/Sema/attr-bounds-safety-function-ptr-param.c
new file mode 100644
index 0000000000000..091220e313958
--- /dev/null
+++ b/clang/test/Sema/attr-bounds-safety-function-ptr-param.c
@@ -0,0 +1,173 @@
+// XFAIL: *
+// FIXME: https://github.com/llvm/llvm-project/issues/166454
+
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s
+
+#define __counted_by(N) __attribute__((counted_by(N)))
+#define __counted_by_or_null(N) __attribute__((counted_by_or_null(N)))
+#define __sized_by(N) __attribute__((sized_by(N)))
+#define __sized_by_or_null(N) __attribute__((sized_by_or_null(N)))
+
+//==============================================================================
+// Test bounds safety attributes on function pointer parameters
+//==============================================================================
+
+struct counted_by_function_pointer_param {
+ // expected-error at +1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(int *__counted_by(len));
+ int len;
+};
+
+struct counted_by_or_null_function_pointer_param {
+ // expected-error at +1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(int *__counted_by_or_null(len));
+ int len;
+};
+
+struct sized_by_function_pointer_param {
+ // expected-error at +1{{'sized_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(char *__sized_by(len));
+ int len;
+};
+
+struct sized_by_or_null_function_pointer_param {
+ // expected-error at +1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(char *__sized_by_or_null(len));
+ int len;
+};
+
+//==============================================================================
+// Test multiple parameters with bounds safety attributes
+//==============================================================================
+
+struct multiple_params_with_bounds_safety {
+ // expected-error at +1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*multi_callback)(int *__counted_by(len1), char *data, int len1);
+ int len1;
+};
+
+struct mixed_bounds_safety_params {
+ // expected-error at +2{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}}
+ // expected-error at +1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*mixed_callback)(int *__counted_by(count), char *__sized_by_or_null(size), int count, int size);
+ int count;
+ int size;
+};
+
+//==============================================================================
+// Test cases that do not require late parsing (count field defined before use)
+//==============================================================================
+
+struct counted_by_no_late_parse {
+ int len;
+ // expected-error at +1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(int *__counted_by(len));
+};
+
+struct counted_by_or_null_no_late_parse {
+ int len;
+ // expected-error at +1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(int *__counted_by_or_null(len));
+};
+
+struct sized_by_no_late_parse {
+ int len;
+ // expected-error at +1{{'sized_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(char *__sized_by(len));
+};
+
+struct sized_by_or_null_no_late_parse {
+ int len;
+ // expected-error at +1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(char *__sized_by_or_null(len));
+};
+
+//==============================================================================
+// Test nested function pointer types
+//==============================================================================
+
+struct nested_function_pointer_with_bounds_safety {
+ // expected-error at +1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*outer_callback)(int (*inner)(int *__counted_by(len)), int len);
+ int len;
+};
+
+//==============================================================================
+// Test struct members with anonymous structs/unions (no late parsing needed)
+//==============================================================================
+
+struct with_anonymous_struct_no_late_parse {
+ int len;
+ // expected-error at +1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(int *__counted_by(len));
+};
+
+struct with_anonymous_union_no_late_parse {
+ union {
+ int len;
+ float f_len;
+ };
+ // expected-error at +1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(int *__counted_by_or_null(len));
+};
+
+//==============================================================================
+// Test with different parameter positions
+//==============================================================================
+
+struct first_param_bounds_safety_no_late_parse {
+ int count;
+ // expected-error at +1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(int *__counted_by(count), void *data, int extra);
+};
+
+struct middle_param_bounds_safety_no_late_parse {
+ int size;
+ // expected-error at +1{{'sized_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(void *prefix, char *__sized_by(size), int suffix);
+};
+
+struct last_param_bounds_safety_no_late_parse {
+ int len;
+ // expected-error at +1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(int a, float b, int *__counted_by_or_null(len));
+};
+
+//==============================================================================
+// Test with const and volatile qualifiers
+//==============================================================================
+
+struct const_param_bounds_safety_no_late_parse {
+ int count;
+ // expected-error at +1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(const int *__counted_by(count));
+};
+
+struct volatile_param_bounds_safety_no_late_parse {
+ int size;
+ // expected-error at +1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(volatile char *__sized_by_or_null(size));
+};
+
+struct const_volatile_param_bounds_safety_no_late_parse {
+ int len;
+ // expected-error at +1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback)(const volatile int *__counted_by_or_null(len));
+};
+
+//==============================================================================
+// Test with multiple function pointers in same struct
+//==============================================================================
+
+struct multiple_function_pointers_no_late_parse {
+ int len1, len2, size1, size2;
+ // expected-error at +1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback1)(int *__counted_by(len1));
+ // expected-error at +1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ int (*callback2)(int *__counted_by_or_null(len2));
+ // expected-error at +1{{'sized_by' attribute cannot be applied to a parameter in a function pointer type}}
+ void (*callback3)(char *__sized_by(size1));
+ // expected-error at +1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}}
+ void (*callback4)(char *__sized_by_or_null(size2));
+};
diff --git a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
index 443ccbbae66db..dad463dd274a6 100644
--- a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
@@ -29,9 +29,8 @@ struct on_member_pointer_const_incomplete_ty {
};
struct on_member_pointer_void_ty {
- // expected-warning at +2{{'counted_by' on a pointer to void is a GNU extension, treated as 'sized_by'}}
- // expected-note at +1{{use '__sized_by' to suppress this warning}}
- void* buf __counted_by(count);
+ // expected-warning at +1{{'counted_by' on a pointer to void is a GNU extension, treated as 'sized_by'}}
+ void* buf __counted_by(count); // expected-note{{use '__sized_by' to suppress this warning}}
int count;
};
@@ -106,119 +105,104 @@ struct on_pointer_anon_count {
//==============================================================================
// __counted_by on struct member pointer in type attribute position
//==============================================================================
-// TODO: Correctly parse counted_by as a type attribute. Currently it is parsed
-// as a declaration attribute and is **not** late parsed resulting in the `count`
-// field being unavailable.
struct on_member_pointer_complete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known *__counted_by(count) buf;
int count;
};
struct on_member_pointer_incomplete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_unknown * __counted_by(count) buf;
int count;
};
struct on_member_pointer_const_incomplete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
const struct size_unknown * __counted_by(count) buf;
int count;
};
struct on_member_pointer_void_ty_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being an incomplete type.
- // expected-error at +1{{use of undeclared identifier 'count'}}
- void *__counted_by(count) buf;
+ // expected-warning at +1{{'counted_by' on a pointer to void is a GNU extension, treated as 'sized_by'}}
+ void *__counted_by(count) buf; // expected-note{{use '__sized_by' to suppress this warning}}
int count;
};
// -
struct on_member_pointer_fn_ptr_ty_pos {
- // TODO: buffer of `count` function pointers should be allowed
- // but fails because this isn't late parsed.
- // expected-error at +1{{use of undeclared identifier 'count'}}
void (** __counted_by(count) fn_ptr)(void);
int count;
};
struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
- // TODO: buffer of `count` function pointers should be allowed
- // but fails because this isn't late parsed.
- // expected-error at +1{{use of undeclared identifier 'count'}}
fn_ptr_ty* __counted_by(count) fn_ptr;
int count;
};
struct on_member_pointer_fn_ty_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a function type.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __counted_by(count) fn_ptr)(void);
int count;
};
struct on_member_pointer_fn_ptr_ty_ty_pos {
- // TODO: buffer of `count` function pointers should be allowed
- // expected-error at +1{{use of undeclared identifier 'count'}}
void (** __counted_by(count) fn_ptr)(void);
int count;
};
struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a function type.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
fn_ptr_ty __counted_by(count) fn_ptr;
int count;
};
struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a function type.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __counted_by(count) * fn_ptr)(void);
int count;
};
+struct on_member_ptr_ptr_fn_ptr_ty_ty_pos_inner {
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ void (**__counted_by(count) * fn_ptr)(void);
+ int count;
+};
+
struct on_member_pointer_struct_with_vla_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a struct type with a VLA.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}}
struct has_unannotated_vla *__counted_by(count) objects;
int count;
};
struct on_member_pointer_struct_with_annotated_vla_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a struct type with a VLA.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}}
struct has_annotated_vla* __counted_by(count) objects;
int count;
};
struct on_nested_pointer_inner {
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__counted_by` can only be nested when used in function parameters.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
struct size_known *__counted_by(count) *buf;
int count;
};
struct on_nested_pointer_outer {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known **__counted_by(count) buf;
int count;
};
+struct on_nested_pointer_array_inner {
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ struct size_known *__counted_by(count) arr[10];
+ int count;
+};
+
+struct on_nested_pointer_flexible_array_inner {
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ struct size_known *__counted_by(count) arr[];
+ int count;
+};
+
struct on_pointer_anon_buf_ty_pos {
struct {
// TODO: Support referring to parent scope
@@ -229,8 +213,6 @@ struct on_pointer_anon_buf_ty_pos {
};
struct on_pointer_anon_count_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known *__counted_by(count) buf;
struct {
int count;
diff --git a/clang/test/Sema/attr-counted-by-or-null-last-field.c b/clang/test/Sema/attr-counted-by-or-null-last-field.c
index d0c50a733acef..9a1cae59f5282 100644
--- a/clang/test/Sema/attr-counted-by-or-null-last-field.c
+++ b/clang/test/Sema/attr-counted-by-or-null-last-field.c
@@ -128,9 +128,7 @@ struct on_member_ptr_incomplete_const_ty_ty_pos {
struct on_member_ptr_void_ty_ty_pos {
int count;
- // expected-warning at +2{{'counted_by_or_null' on a pointer to void is a GNU extension, treated as 'sized_by_or_null'}}
- // expected-note at +1{{use '__sized_by_or_null' to suppress this warning}}
- void * ptr __counted_by_or_null(count);
+ void * ptr __counted_by_or_null(count); // expected-warning{{'counted_by_or_null' on a pointer to void is a GNU extension, treated as 'sized_by_or_null'}} expected-note{{use '__sized_by_or_null' to suppress this warning}}
};
typedef void(fn_ty)(int);
diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
index 233b729f87ccd..a6e4c177042bc 100644
--- a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
@@ -30,9 +30,7 @@ struct on_member_pointer_const_incomplete_ty {
};
struct on_member_pointer_void_ty {
- // expected-warning at +2{{'counted_by_or_null' on a pointer to void is a GNU extension, treated as 'sized_by_or_null'}}
- // expected-note at +1{{use '__sized_by_or_null' to suppress this warning}}
- void* buf __counted_by_or_null(count);
+ void* buf __counted_by_or_null(count); // expected-warning{{'counted_by_or_null' on a pointer to void is a GNU extension, treated as 'sized_by_or_null'}} expected-note{{use '__sized_by_or_null' to suppress this warning}}
int count;
};
@@ -107,115 +105,87 @@ struct on_pointer_anon_count {
//==============================================================================
// __counted_by_or_null on struct member pointer in type attribute position
//==============================================================================
-// TODO: Correctly parse counted_by_or_null as a type attribute. Currently it is parsed
-// as a declaration attribute and is **not** late parsed resulting in the `count`
-// field being unavailable.
struct on_member_pointer_complete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known *__counted_by_or_null(count) buf;
int count;
};
struct on_member_pointer_incomplete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_unknown * __counted_by_or_null(count) buf;
int count;
};
struct on_member_pointer_const_incomplete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
const struct size_unknown * __counted_by_or_null(count) buf;
int count;
};
struct on_member_pointer_void_ty_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being an incomplete type.
- // expected-error at +1{{use of undeclared identifier 'count'}}
- void *__counted_by_or_null(count) buf;
+ void *__counted_by_or_null(count) buf; // expected-warning{{'counted_by_or_null' on a pointer to void is a GNU extension, treated as 'sized_by_or_null'}} expected-note{{use '__sized_by_or_null' to suppress this warning}}
int count;
};
// -
struct on_member_pointer_fn_ptr_ty_pos {
- // TODO: buffer of `count` function pointers should be allowed
- // but fails because this isn't late parsed.
- // expected-error at +1{{use of undeclared identifier 'count'}}
void (** __counted_by_or_null(count) fn_ptr)(void);
int count;
};
struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
- // TODO: buffer of `count` function pointers should be allowed
- // but fails because this isn't late parsed.
- // expected-error at +1{{use of undeclared identifier 'count'}}
fn_ptr_ty* __counted_by_or_null(count) fn_ptr;
int count;
};
struct on_member_pointer_fn_ty_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a function type.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __counted_by_or_null(count) fn_ptr)(void);
int count;
};
struct on_member_pointer_fn_ptr_ty_ty_pos {
- // TODO: buffer of `count` function pointers should be allowed
- // expected-error at +1{{use of undeclared identifier 'count'}}
void (** __counted_by_or_null(count) fn_ptr)(void);
int count;
};
struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a function type.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
fn_ptr_ty __counted_by_or_null(count) fn_ptr;
int count;
};
struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a function type.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __counted_by_or_null(count) * fn_ptr)(void);
int count;
};
+struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner {
+ // expected-error at +1{{'counted_by_or_null' attribute on nested pointer type is not allowed}}
+ void (** __counted_by_or_null(count) * fn_ptr)(void);
+ int count;
+};
+
struct on_member_pointer_struct_with_vla_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a struct type with a VLA.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}}
struct has_unannotated_vla *__counted_by_or_null(count) objects;
int count;
};
struct on_member_pointer_struct_with_annotated_vla_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a struct type with a VLA.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}}
struct has_annotated_vla* __counted_by_or_null(count) objects;
int count;
};
struct on_nested_pointer_inner {
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__counted_by_or_null` can only be nested when used in function parameters.
- // expected-error at +1{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{'counted_by_or_null' attribute on nested pointer type is not allowed}}
struct size_known *__counted_by_or_null(count) *buf;
int count;
};
struct on_nested_pointer_outer {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known **__counted_by_or_null(count) buf;
int count;
};
@@ -230,8 +200,6 @@ struct on_pointer_anon_buf_ty_pos {
};
struct on_pointer_anon_count_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known *__counted_by_or_null(count) buf;
struct {
int count;
diff --git a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c
index cff5a14c70b99..4d4a6e81e3766 100644
--- a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c
+++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c
@@ -17,7 +17,7 @@
// expected-note at +1 24{{forward declaration of 'struct IncompleteTy'}}
struct IncompleteTy; // expected-note 27{{consider providing a complete definition for 'struct IncompleteTy'}}
-typedef struct IncompleteTy Incomplete_t;
+typedef struct IncompleteTy Incomplete_t;
struct CBBufDeclPos {
int count;
@@ -75,7 +75,7 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
void* tmp3 = implicit_full_init.buf;
// expected-error at +1{{cannot use 'implicit_full_init.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
void* tmp4 = implicit_full_init.buf_typedef;
-
+
struct CBBufDeclPos explicit_non_desig_init = {
0,
// expected-error at +1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
@@ -113,7 +113,7 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
uninit.buf_typedef++; // // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
++uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
-
+
uninit.buf--; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
--uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
uninit.buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
@@ -139,16 +139,16 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
// ## Use of fields in expressions
// ===========================================================================
// expected-error at +2{{cannot use 'uninit.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
- void* addr =
+ void* addr =
((char*) uninit.buf ) + 1;
// expected-error at +2{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
- void* addr_typedef =
+ void* addr_typedef =
((char*) uninit.buf_typedef ) + 1;
// expected-error at +2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
- void* addr_ptr =
+ void* addr_ptr =
((char*) ptr->buf ) + 1;
// expected-error at +2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
- void* addr_ptr_typedef =
+ void* addr_ptr_typedef =
((char*) ptr->buf_typedef ) + 1;
@@ -289,7 +289,7 @@ void test_CBBufDeclPos_completed(struct CBBufDeclPos* ptr) {
};
struct CBBufDeclPos implicit_full_init = {0};
-
+
struct CBBufDeclPos explicit_non_desig_init = {
0,
0x0,
@@ -384,10 +384,10 @@ void use_CBBufTyPos(struct CBBufTyPos* ptr) {
// Use
// expected-error at +2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
- void* addr =
+ void* addr =
((char*) ptr->buf ) + 1;
// expected-error at +2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
- void* addr_typedef =
+ void* addr_typedef =
((char*) ptr->buf_typedef ) + 1;
// expected-error at +1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
@@ -458,10 +458,10 @@ void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) {
// Use
// expected-error at +2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
- void* addr =
+ void* addr =
((char*) ptr->buf ) + 1;
// expected-error at +2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}}
- void* addr_typedef =
+ void* addr_typedef =
((char*) ptr->buf_typedef ) + 1;
// expected-error at +1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
@@ -532,10 +532,10 @@ void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) {
// Use
// expected-error at +2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
- void* addr =
+ void* addr =
((char*) ptr->buf ) + 1;
// expected-error at +2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
- void* addr_typedef =
+ void* addr_typedef =
((char*) ptr->buf_typedef ) + 1;
// expected-error at +1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
@@ -616,16 +616,13 @@ struct IncompleteTy3;
struct CBBufFAMofCountedByPtrs {
int size;
- // TODO: This is misleading. The attribute is written in the type position
- // but clang currently doesn't treat it like that and it gets treated as
- // an attribute on the array, rather than on the element type.
- // expected-error at +1{{'counted_by_or_null' only applies to pointers; did you mean to use 'counted_by'?}}
+ // expected-error at +1{{'counted_by_or_null' attribute on nested pointer type is not allowed}}
struct IncompleteTy3* __counted_by_or_null(size) arr[];
};
void arr_of_counted_by_ptr(struct CBBufFAMofCountedByPtrs* ptr) {
- // TODO: Should be disallowed once parsing attributes in the type position
- // works.
+ // TODO: Diagnostic should appear here once `__counted_by_or_null` is allowed on
+ // nested pointers.
ptr->arr[0] = 0x0;
void* addr = ((char*) ptr->arr[0]) + 1;
}
diff --git a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
index 0fd739ca7d4c3..e8ab29cd80181 100644
--- a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
@@ -105,8 +105,6 @@ struct on_pointer_anon_count {
//==============================================================================
// __counted_by_or_null on struct member pointer in type attribute position
//==============================================================================
-// TODO: Correctly parse counted_by_or_null as a type attribute. Currently it is parsed
-// as a declaration attribute
struct on_member_pointer_complete_ty_ty_pos {
int count;
@@ -158,13 +156,18 @@ struct on_member_pointer_fn_ptr_ty_ty_pos {
fn_ptr_ty __counted_by_or_null(count) fn_ptr;
};
-// TODO: This should be forbidden but isn't due to counted_by_or_null being treated
-// as a declaration attribute.
struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
int count;
+ // expected-error at +1{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __counted_by_or_null(count) * fn_ptr)(void);
};
+struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner {
+ int count;
+ // expected-error at +1{{'counted_by_or_null' attribute on nested pointer type is not allowed}}
+ void (** __counted_by_or_null(count) * fn_ptr)(void);
+};
+
struct on_member_pointer_struct_with_vla_ty_pos {
int count;
// expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}}
@@ -181,9 +184,8 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos {
};
struct on_nested_pointer_inner {
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__counted_by_or_null` can only be nested when used in function parameters.
int count;
+ // expected-error at +1{{'counted_by_or_null' attribute on nested pointer type is not allowed}}
struct size_known *__counted_by_or_null(count) *buf;
};
diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
index d28a2086b51b8..f9afe558d0e13 100644
--- a/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
+++ b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
@@ -17,7 +17,7 @@
// expected-note at +1 24{{forward declaration of 'struct IncompleteTy'}}
struct IncompleteTy; // expected-note 27{{consider providing a complete definition for 'struct IncompleteTy'}}
-typedef struct IncompleteTy Incomplete_t;
+typedef struct IncompleteTy Incomplete_t;
struct CBBufDeclPos {
int count;
@@ -75,7 +75,7 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
void* tmp3 = implicit_full_init.buf;
// expected-error at +1{{cannot use 'implicit_full_init.buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
void* tmp4 = implicit_full_init.buf_typedef;
-
+
struct CBBufDeclPos explicit_non_desig_init = {
0,
// expected-error at +1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
@@ -113,7 +113,7 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
uninit.buf_typedef++; // // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
++uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
-
+
uninit.buf--; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
--uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
uninit.buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
@@ -139,16 +139,16 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
// ## Use of fields in expressions
// ===========================================================================
// expected-error at +2{{cannot use 'uninit.buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
- void* addr =
+ void* addr =
((char*) uninit.buf ) + 1;
// expected-error at +2{{cannot use 'uninit.buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
- void* addr_typedef =
+ void* addr_typedef =
((char*) uninit.buf_typedef ) + 1;
// expected-error at +2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
- void* addr_ptr =
+ void* addr_ptr =
((char*) ptr->buf ) + 1;
// expected-error at +2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
- void* addr_ptr_typedef =
+ void* addr_ptr_typedef =
((char*) ptr->buf_typedef ) + 1;
@@ -289,7 +289,7 @@ void test_CBBufDeclPos_completed(struct CBBufDeclPos* ptr) {
};
struct CBBufDeclPos implicit_full_init = {0};
-
+
struct CBBufDeclPos explicit_non_desig_init = {
0,
0x0,
@@ -384,10 +384,10 @@ void use_CBBufTyPos(struct CBBufTyPos* ptr) {
// Use
// expected-error at +2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
- void* addr =
+ void* addr =
((char*) ptr->buf ) + 1;
// expected-error at +2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
- void* addr_typedef =
+ void* addr_typedef =
((char*) ptr->buf_typedef ) + 1;
// expected-error at +1{{cannot use 'ptr->buf' with '__counted_by' attributed type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
@@ -458,10 +458,10 @@ void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) {
// Use
// expected-error at +2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
- void* addr =
+ void* addr =
((char*) ptr->buf ) + 1;
// expected-error at +2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}}
- void* addr_typedef =
+ void* addr_typedef =
((char*) ptr->buf_typedef ) + 1;
// expected-error at +1{{cannot use 'ptr->buf' with '__counted_by' attributed type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
@@ -532,10 +532,10 @@ void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) {
// Use
// expected-error at +2{{cannot use 'ptr->buf' with '__counted_by' attributed type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
- void* addr =
+ void* addr =
((char*) ptr->buf ) + 1;
// expected-error at +2{{cannot use 'ptr->buf_typedef' with '__counted_by' attributed type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
- void* addr_typedef =
+ void* addr_typedef =
((char*) ptr->buf_typedef ) + 1;
// expected-error at +1{{cannot use 'ptr->buf' with '__counted_by' attributed type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
@@ -616,9 +616,7 @@ struct IncompleteTy3;
struct CBBufFAMofCountedByPtrs {
int size;
- // TODO: This is misleading. The attribute is written in the type position
- // but clang currently doesn't treat it like that and it gets treated as
- // an attribute on the array, rather than on the element type.
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
struct IncompleteTy3* __counted_by(size) arr[];
};
diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs.c b/clang/test/Sema/attr-counted-by-struct-ptrs.c
index a42f3895695a3..f8957ca8b4eba 100644
--- a/clang/test/Sema/attr-counted-by-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-struct-ptrs.c
@@ -104,8 +104,6 @@ struct on_pointer_anon_count {
//==============================================================================
// __counted_by on struct member pointer in type attribute position
//==============================================================================
-// TODO: Correctly parse counted_by as a type attribute. Currently it is parsed
-// as a declaration attribute
struct on_member_pointer_complete_ty_ty_pos {
int count;
@@ -157,11 +155,16 @@ struct on_member_pointer_fn_ptr_ty_ty_pos {
fn_ptr_ty __counted_by(count) fn_ptr;
};
-// TODO: This should be forbidden but isn't due to counted_by being treated
-// as a declaration attribute.
struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
int count;
- void (* __counted_by(count) * fn_ptr)(void);
+ // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
+ void (* __counted_by(count) * fn_ptr)(void); // FIXME
+};
+
+struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner {
+ int count;
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ void (** __counted_by(count) * fn_ptr)(void);
};
struct on_member_pointer_struct_with_vla_ty_pos {
@@ -180,10 +183,9 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos {
};
struct on_nested_pointer_inner {
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__counted_by` can only be nested when used in function parameters.
int count;
- struct size_known *__counted_by(count) *buf;
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ struct size_known *__counted_by(count) *buf; // FIXME
};
struct on_nested_pointer_outer {
diff --git a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
index 07f8801787d66..8b4fef63a89c7 100644
--- a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
@@ -102,35 +102,23 @@ struct on_pointer_anon_count {
//==============================================================================
// __sized_by on struct member pointer in type attribute position
//==============================================================================
-// TODO: Correctly parse sized_by as a type attribute. Currently it is parsed
-// as a declaration attribute and is **not** late parsed resulting in the `size`
-// field being unavailable.
struct on_member_pointer_complete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known *__sized_by(size) buf;
int size;
};
struct on_member_pointer_incomplete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_unknown * __sized_by(size) buf;
int size;
};
struct on_member_pointer_const_incomplete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
const struct size_unknown * __sized_by(size) buf;
int size;
};
struct on_member_pointer_void_ty_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being an incomplete type.
- // expected-error at +1{{use of undeclared identifier 'size'}}
void *__sized_by(size) buf;
int size;
};
@@ -138,75 +126,63 @@ struct on_member_pointer_void_ty_ty_pos {
// -
struct on_member_pointer_fn_ptr_ty_pos {
- // TODO: buffer of `size` function pointers should be allowed
- // but fails because this isn't late parsed.
- // expected-error at +1{{use of undeclared identifier 'size'}}
void (** __sized_by(size) fn_ptr)(void);
int size;
};
struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
- // TODO: buffer of `size` function pointers should be allowed
- // but fails because this isn't late parsed.
- // expected-error at +1{{use of undeclared identifier 'size'}}
fn_ptr_ty* __sized_by(size) fn_ptr;
int size;
};
struct on_member_pointer_fn_ty_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a function type.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __sized_by(size) fn_ptr)(void);
int size;
};
struct on_member_pointer_fn_ptr_ty_ty_pos {
- // TODO: buffer of `size` function pointers should be allowed
- // expected-error at +1{{use of undeclared identifier 'size'}}
void (** __sized_by(size) fn_ptr)(void);
int size;
};
struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
- // TODO: This should be allowed with sized_by.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
fn_ptr_ty __sized_by(size) fn_ptr;
int size;
};
struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
- // TODO: This should be allowed with sized_by.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __sized_by(size) * fn_ptr)(void);
int size;
};
+struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner {
+ // expected-error at +1{{'sized_by' attribute on nested pointer type is not allowed}}
+ void (** __sized_by(size) * fn_ptr)(void);
+ int size;
+};
+
struct on_member_pointer_struct_with_vla_ty_pos {
- // TODO: This should be allowed with sized_by.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}}
struct has_unannotated_vla *__sized_by(size) objects;
int size;
};
struct on_member_pointer_struct_with_annotated_vla_ty_pos {
- // TODO: This should be allowed with sized_by.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}}
struct has_annotated_vla* __sized_by(size) objects;
int size;
};
struct on_nested_pointer_inner {
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__sized_by` can only be nested when used in function parameters.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{'sized_by' attribute on nested pointer type is not allowed}}
struct size_known *__sized_by(size) *buf;
int size;
};
struct on_nested_pointer_outer {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known **__sized_by(size) buf;
int size;
};
@@ -221,8 +197,6 @@ struct on_pointer_anon_buf_ty_pos {
};
struct on_pointer_anon_count_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known *__sized_by(size) buf;
struct {
int size;
diff --git a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
index afe5f0af28083..34aaec9ec83d1 100644
--- a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
@@ -102,35 +102,23 @@ struct on_pointer_anon_count {
//==============================================================================
// __sized_by_or_null on struct member pointer in type attribute position
//==============================================================================
-// TODO: Correctly parse sized_by_or_null as a type attribute. Currently it is parsed
-// as a declaration attribute and is **not** late parsed resulting in the `size`
-// field being unavailable.
struct on_member_pointer_complete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known *__sized_by_or_null(size) buf;
int size;
};
struct on_member_pointer_incomplete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_unknown * __sized_by_or_null(size) buf;
int size;
};
struct on_member_pointer_const_incomplete_ty_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
const struct size_unknown * __sized_by_or_null(size) buf;
int size;
};
struct on_member_pointer_void_ty_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being an incomplete type.
- // expected-error at +1{{use of undeclared identifier 'size'}}
void *__sized_by_or_null(size) buf;
int size;
};
@@ -138,75 +126,66 @@ struct on_member_pointer_void_ty_ty_pos {
// -
struct on_member_pointer_fn_ptr_ty_pos {
- // TODO: buffer of `size` function pointers should be allowed
- // but fails because this isn't late parsed.
- // expected-error at +1{{use of undeclared identifier 'size'}}
void (** __sized_by_or_null(size) fn_ptr)(void);
int size;
};
struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
- // TODO: buffer of `size` function pointers should be allowed
- // but fails because this isn't late parsed.
- // expected-error at +1{{use of undeclared identifier 'size'}}
fn_ptr_ty* __sized_by_or_null(size) fn_ptr;
int size;
};
struct on_member_pointer_fn_ty_ty_pos {
- // TODO: This should fail because the attribute is
- // on a pointer with the pointee being a function type.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // TODO: Improve diagnostics (Issue #167368).
+ // expected-error at +1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __sized_by_or_null(size) fn_ptr)(void);
int size;
};
struct on_member_pointer_fn_ptr_ty_ty_pos {
- // TODO: buffer of `size` function pointers should be allowed
- // expected-error at +1{{use of undeclared identifier 'size'}}
void (** __sized_by_or_null(size) fn_ptr)(void);
int size;
};
struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
- // TODO: This should be allowed with sized_by_or_null.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
fn_ptr_ty __sized_by_or_null(size) fn_ptr;
int size;
};
struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
- // TODO: This should be allowed with sized_by_or_null.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __sized_by_or_null(size) * fn_ptr)(void);
int size;
};
+struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner {
+ // expected-error at +1{{'sized_by_or_null' attribute on nested pointer type is not allowed}}
+ void (** __sized_by_or_null(size) * fn_ptr)(void);
+ int size;
+};
+
struct on_member_pointer_struct_with_vla_ty_pos {
- // TODO: This should be allowed with sized_by_or_null.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // TODO: Allow this
+ // expected-error at +1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}}
struct has_unannotated_vla *__sized_by_or_null(size) objects;
int size;
};
struct on_member_pointer_struct_with_annotated_vla_ty_pos {
- // TODO: This should be allowed with sized_by_or_null.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // TODO: Allow this
+ // expected-error at +1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}}
struct has_annotated_vla* __sized_by_or_null(size) objects;
int size;
};
struct on_nested_pointer_inner {
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__sized_by_or_null` can only be nested when used in function parameters.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{'sized_by_or_null' attribute on nested pointer type is not allowed}}
struct size_known *__sized_by_or_null(size) *buf;
int size;
};
struct on_nested_pointer_outer {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known **__sized_by_or_null(size) buf;
int size;
};
@@ -221,8 +200,6 @@ struct on_pointer_anon_buf_ty_pos {
};
struct on_pointer_anon_count_ty_pos {
- // TODO: Allow this
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known *__sized_by_or_null(size) buf;
struct {
int size;
diff --git a/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c b/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
index 4200c9275a180..fc4aee3730f89 100644
--- a/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
@@ -102,8 +102,6 @@ struct on_pointer_anon_size {
//==============================================================================
// __sized_by_or_null on struct member pointer in type attribute position
//==============================================================================
-// TODO: Correctly parse sized_by_or_null as a type attribute. Currently it is parsed
-// as a declaration attribute
struct on_member_pointer_complete_ty_ty_pos {
int size;
@@ -151,13 +149,18 @@ struct on_member_pointer_fn_ptr_ty_ty_pos {
fn_ptr_ty __sized_by_or_null(size) fn_ptr;
};
-// TODO: This should be forbidden but isn't due to sized_by_or_null being treated
-// as a declaration attribute.
struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
int size;
+ // expected-error at +1{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __sized_by_or_null(size) * fn_ptr)(void);
};
+struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner {
+ int size;
+ // expected-error at +1{{'sized_by_or_null' attribute on nested pointer type is not allowed}}
+ void (** __sized_by_or_null(size) * fn_ptr)(void);
+};
+
struct on_member_pointer_struct_with_vla_ty_pos {
int size;
// expected-error at +1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}}
@@ -171,9 +174,8 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos {
};
struct on_nested_pointer_inner {
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__sized_by_or_null` can only be nested when used in function parameters.
int size;
+ // expected-error at +1{{'sized_by_or_null' attribute on nested pointer type is not allowed}}
struct size_known *__sized_by_or_null(size) *buf;
};
diff --git a/clang/test/Sema/attr-sized-by-struct-ptrs.c b/clang/test/Sema/attr-sized-by-struct-ptrs.c
index 07373b247d0f7..49034685a9a59 100644
--- a/clang/test/Sema/attr-sized-by-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-struct-ptrs.c
@@ -151,13 +151,18 @@ struct on_member_pointer_fn_ptr_ty_ty_pos {
fn_ptr_ty __sized_by(size) fn_ptr;
};
-// TODO: This should be forbidden but isn't due to sized_by being treated
-// as a declaration attribute.
struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
int size;
+ // expected-error at +1{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}}
void (* __sized_by(size) * fn_ptr)(void);
};
+struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner {
+ int size;
+ // expected-error at +1{{'sized_by' attribute on nested pointer type is not allowed}}
+ void (** __sized_by(size) * fn_ptr)(void);
+};
+
struct on_member_pointer_struct_with_vla_ty_pos {
int size;
// expected-error at +1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}}
@@ -171,9 +176,8 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos {
};
struct on_nested_pointer_inner {
- // TODO: This should be disallowed because in the `-fbounds-safety` model
- // `__sized_by` can only be nested when used in function parameters.
int size;
+ // expected-error at +1{{'sized_by' attribute on nested pointer type is not allowed}}
struct size_known *__sized_by(size) *buf;
};
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 32e84248c1b27..6053555781b57 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1744,6 +1744,10 @@ bool CursorVisitor::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) {
return Visit(TL.getInnerLoc());
}
+bool CursorVisitor::VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
+ return Visit(TL.getInnerLoc());
+}
+
bool CursorVisitor::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
return Visit(TL.getWrappedLoc());
}
>From 07075fc750e26ff008ece883913c048e83c32616 Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Sat, 7 Feb 2026 06:03:22 -0800
Subject: [PATCH 02/10] Add serialization testing for late parsed type
attributes
---
...ounds-safety-attributed-type-late-parsed.c | 111 +++++
...ounds-safety-attributed-type-late-parsed.h | 58 +++
...ounds-safety-attributed-type-late-parsed.c | 69 +++
...nted-by-weird-type-positions-late-parsed.c | 456 ++++++++++++++++++
.../attr-counted-by-weird-type-positions.c | 454 +++++++++++++++++
5 files changed, 1148 insertions(+)
create mode 100644 clang/test/Modules/bounds-safety-attributed-type-late-parsed.c
create mode 100644 clang/test/PCH/Inputs/bounds-safety-attributed-type-late-parsed.h
create mode 100644 clang/test/PCH/bounds-safety-attributed-type-late-parsed.c
create mode 100644 clang/test/Sema/attr-counted-by-weird-type-positions-late-parsed.c
create mode 100644 clang/test/Sema/attr-counted-by-weird-type-positions.c
diff --git a/clang/test/Modules/bounds-safety-attributed-type-late-parsed.c b/clang/test/Modules/bounds-safety-attributed-type-late-parsed.c
new file mode 100644
index 0000000000000..58df0761f2022
--- /dev/null
+++ b/clang/test/Modules/bounds-safety-attributed-type-late-parsed.c
@@ -0,0 +1,111 @@
+// Test serialization of late-parsed bounds-safety attributes via Modules
+// This verifies that LateParsedAttrType is transformed to CountAttributedType
+// before serialization and remains as CountAttributedType after deserialization.
+
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fmodules -fmodules-cache-path=%t -verify %s
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fmodules -fmodules-cache-path=%t -ast-dump-all %s | FileCheck %s
+// expected-no-diagnostics
+
+#pragma clang module build bounds_safety_late_parsed
+module bounds_safety_late_parsed {}
+#pragma clang module contents
+#pragma clang module begin bounds_safety_late_parsed
+
+// Test where counted_by references a field declared later
+struct LateRefPointer {
+ int *__attribute__((counted_by(count))) buf;
+ int count;
+};
+
+// Test with sized_by referencing later field
+struct LateRefSized {
+ int *__attribute__((sized_by(size))) data;
+ int size;
+};
+
+// Test with counted_by_or_null referencing later field
+struct LateRefCountedByOrNull {
+ int *__attribute__((counted_by_or_null(count))) buf;
+ int count;
+};
+
+// Test with sized_by_or_null referencing later field
+struct LateRefSizedByOrNull {
+ int *__attribute__((sized_by_or_null(size))) data;
+ int size;
+};
+
+// Test with nested struct
+struct LateRefNested {
+ struct Inner {
+ int value;
+ } *__attribute__((counted_by(n))) items;
+ int n;
+};
+
+// Test with multiple late-parsed attributes
+struct MultipleLateRefs {
+ int *__attribute__((counted_by(count1))) buf1;
+ int *__attribute__((sized_by(count2))) buf2;
+ int *__attribute__((counted_by_or_null(count3))) buf3;
+ int *__attribute__((sized_by_or_null(count4))) buf4;
+ int count1;
+ int count2;
+ int count3;
+ int count4;
+};
+
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module import bounds_safety_late_parsed
+
+struct LateRefPointer *p1;
+struct LateRefSized *p2;
+struct LateRefCountedByOrNull *p3;
+struct LateRefSizedByOrNull *p4;
+struct LateRefNested *p5;
+struct MultipleLateRefs *p6;
+
+// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefPointer definition
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf 'int * __counted_by(count)':'int *'
+// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count 'int'
+
+// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefSized definition
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed data 'int * __sized_by(size)':'int *'
+// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced size 'int'
+
+// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefCountedByOrNull definition
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf 'int * __counted_by_or_null(count)':'int *'
+// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count 'int'
+
+// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefSizedByOrNull definition
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed data 'int * __sized_by_or_null(size)':'int *'
+// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced size 'int'
+
+// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefNested definition
+// CHECK: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed items 'struct Inner * __counted_by(n)':'struct Inner *'
+// CHECK: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced n 'int'
+
+// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct MultipleLateRefs definition
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf1 'int * __counted_by(count1)':'int *'
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf2 'int * __sized_by(count2)':'int *'
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf3 'int * __counted_by_or_null(count3)':'int *'
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf4 'int * __sized_by_or_null(count4)':'int *'
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count1 'int'
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count2 'int'
+// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count3 'int'
+// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count4 'int'
+
+// Verify that LateParsedAttrType does not appear in the AST dump
+// CHECK-NOT: LateParsedAttr
+
+// Verify the import and variable declarations
+// CHECK: ImportDecl {{.*}} implicit bounds_safety_late_parsed
+// CHECK: VarDecl {{.*}} p1 'struct LateRefPointer *'
+// CHECK: VarDecl {{.*}} p2 'struct LateRefSized *'
+// CHECK: VarDecl {{.*}} p3 'struct LateRefCountedByOrNull *'
+// CHECK: VarDecl {{.*}} p4 'struct LateRefSizedByOrNull *'
+// CHECK: VarDecl {{.*}} p5 'struct LateRefNested *'
+// CHECK: VarDecl {{.*}} p6 'struct MultipleLateRefs *'
diff --git a/clang/test/PCH/Inputs/bounds-safety-attributed-type-late-parsed.h b/clang/test/PCH/Inputs/bounds-safety-attributed-type-late-parsed.h
new file mode 100644
index 0000000000000..c0751408045c7
--- /dev/null
+++ b/clang/test/PCH/Inputs/bounds-safety-attributed-type-late-parsed.h
@@ -0,0 +1,58 @@
+// Header for testing late-parsed bounds-safety attributes serialization
+
+#define __counted_by(f) __attribute__((counted_by(f)))
+#define __sized_by(f) __attribute__((sized_by(f)))
+#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f)))
+#define __sized_by_or_null(f) __attribute__((sized_by_or_null(f)))
+
+// Test where counted_by references a field declared later
+struct LateRefPointer {
+ int *__counted_by(count) buf;
+ int count;
+};
+
+// Test with sized_by referencing later field
+struct LateRefSized {
+ int *__sized_by(size) data;
+ int size;
+};
+
+// Test with counted_by_or_null referencing later field
+struct LateRefCountedByOrNull {
+ int *__counted_by_or_null(count) buf;
+ int count;
+};
+
+// Test with sized_by_or_null referencing later field
+struct LateRefSizedByOrNull {
+ int *__sized_by_or_null(size) data;
+ int size;
+};
+
+// Test with nested struct
+struct LateRefNested {
+ struct Inner {
+ int value;
+ } *__counted_by(n) items;
+ int n;
+};
+
+// Test with multiple late-parsed attributes
+struct MultipleLateRefs {
+ int *__counted_by(count1) buf1;
+ int *__sized_by(count2) buf2;
+ int *__counted_by_or_null(count3) buf3;
+ int *__sized_by_or_null(count4) buf4;
+ int count1;
+ int count2;
+ int count3;
+ int count4;
+};
+
+// Test with anonymous struct/union
+struct LateRefAnon {
+ int *__counted_by(count) buf;
+ struct {
+ int count;
+ };
+};
diff --git a/clang/test/PCH/bounds-safety-attributed-type-late-parsed.c b/clang/test/PCH/bounds-safety-attributed-type-late-parsed.c
new file mode 100644
index 0000000000000..1d77bbe13927c
--- /dev/null
+++ b/clang/test/PCH/bounds-safety-attributed-type-late-parsed.c
@@ -0,0 +1,69 @@
+// Test serialization of late-parsed bounds-safety attributes via PCH
+// This verifies that LateParsedAttrType is transformed to CountAttributedType
+// before serialization and remains as CountAttributedType after deserialization.
+
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -include %S/Inputs/bounds-safety-attributed-type-late-parsed.h -fsyntax-only -verify %s
+
+// Test with pch.
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -emit-pch -o %t %S/Inputs/bounds-safety-attributed-type-late-parsed.h
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -include-pch %t -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -include-pch %t -ast-print %s | FileCheck %s --check-prefix PRINT
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -include-pch %t -ast-dump-all %s | FileCheck %s --check-prefix DUMP
+// expected-no-diagnostics
+
+// PRINT: struct LateRefPointer {
+// PRINT-NEXT: int * __counted_by(count)buf;
+// PRINT-NEXT: int count;
+// PRINT-NEXT: };
+
+// PRINT: struct LateRefSized {
+// PRINT-NEXT: int * __sized_by(size)data;
+// PRINT-NEXT: int size;
+// PRINT-NEXT: };
+
+// PRINT: struct LateRefCountedByOrNull {
+// PRINT-NEXT: int * __counted_by_or_null(count)buf;
+// PRINT-NEXT: int count;
+// PRINT-NEXT: };
+
+// PRINT: struct LateRefSizedByOrNull {
+// PRINT-NEXT: int * __sized_by_or_null(size)data;
+// PRINT-NEXT: int size;
+// PRINT-NEXT: };
+
+// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefPointer definition
+// DUMP-NEXT: |-FieldDecl {{.*}} imported buf 'int * __counted_by(count)':'int *'
+// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced count 'int'
+
+// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefSized definition
+// DUMP-NEXT: |-FieldDecl {{.*}} imported data 'int * __sized_by(size)':'int *'
+// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced size 'int'
+
+// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefCountedByOrNull definition
+// DUMP-NEXT: |-FieldDecl {{.*}} imported buf 'int * __counted_by_or_null(count)':'int *'
+// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced count 'int'
+
+// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefSizedByOrNull definition
+// DUMP-NEXT: |-FieldDecl {{.*}} imported data 'int * __sized_by_or_null(size)':'int *'
+// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced size 'int'
+
+// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefNested definition
+// DUMP: |-FieldDecl {{.*}} imported items 'struct Inner * __counted_by(n)':'struct Inner *'
+// DUMP: `-FieldDecl {{.*}} imported referenced n 'int'
+
+// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct MultipleLateRefs definition
+// DUMP-NEXT: |-FieldDecl {{.*}} imported buf1 'int * __counted_by(count1)':'int *'
+// DUMP-NEXT: |-FieldDecl {{.*}} imported buf2 'int * __sized_by(count2)':'int *'
+// DUMP-NEXT: |-FieldDecl {{.*}} imported buf3 'int * __counted_by_or_null(count3)':'int *'
+// DUMP-NEXT: |-FieldDecl {{.*}} imported buf4 'int * __sized_by_or_null(count4)':'int *'
+// DUMP-NEXT: |-FieldDecl {{.*}} imported referenced count1 'int'
+// DUMP-NEXT: |-FieldDecl {{.*}} imported referenced count2 'int'
+// DUMP-NEXT: |-FieldDecl {{.*}} imported referenced count3 'int'
+// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced count4 'int'
+
+// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefAnon definition
+// DUMP-NEXT: |-FieldDecl {{.*}} imported buf 'int * __counted_by(count)':'int *'
+// DUMP: `-IndirectFieldDecl {{.*}} imported implicit referenced count 'int'
+
+// Verify that LateParsedAttrType does not appear in the AST dump
+// DUMP-NOT: LateParsedAttr
diff --git a/clang/test/Sema/attr-counted-by-weird-type-positions-late-parsed.c b/clang/test/Sema/attr-counted-by-weird-type-positions-late-parsed.c
new file mode 100644
index 0000000000000..0728c8623c202
--- /dev/null
+++ b/clang/test/Sema/attr-counted-by-weird-type-positions-late-parsed.c
@@ -0,0 +1,456 @@
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s
+
+#define __counted_by(f) __attribute__((counted_by(f)))
+
+// ============================================================================
+// SIMPLE POINTER: int *buf
+// ============================================================================
+
+// Position: after *, before identifier
+// Applies to `int *`.
+struct ptr_after_star {
+ int *__counted_by(count) buf;
+ int count;
+};
+
+// Position: before type specifier
+// Applies to the top-level type.
+struct ptr_before_type {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ __counted_by(count) int *buf;
+ int count;
+};
+
+// Position: after type, before *
+// Applies to `int`.
+struct ptr_after_type {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) *buf;
+ int count;
+};
+
+// Position: after identifier
+// Applies to the top-level type.
+struct ptr_after_ident {
+ int *buf __counted_by(count);
+ int count;
+};
+
+// ============================================================================
+// TYPEDEF POINTER: ptr_to_int_t buf
+// ============================================================================
+
+typedef int * ptr_to_int_t;
+
+// Position: after typedef name, before identifier
+// Applies to `ptr_to_int_t`.
+struct typedef_after_type {
+ ptr_to_int_t __counted_by(count) buf;
+ int count;
+};
+
+// Position: before typedef name
+// Applies to the top-level type.
+struct typedef_before_type {
+ __counted_by(count) ptr_to_int_t buf;
+ int count;
+};
+
+// Position: after identifier
+// Applies to the top-level type.
+struct typedef_after_ident {
+ ptr_to_int_t buf __counted_by(count);
+ int count;
+};
+
+// ============================================================================
+// POINTER TO ARRAY: int (*buf)[4]
+// ============================================================================
+
+// Position: after type, before (*...)
+// Applies to `int`.
+struct ptr_to_arr_after_type {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* buf)[4];
+ int count;
+};
+
+// Position: before type
+// Applies to the top-level type.
+struct ptr_to_arr_before_type {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ __counted_by(count) int (* buf)[4];
+ int count;
+};
+
+// Position: after *, before identifier (inside parens)
+// Applies to `int (*)[4]`.
+struct ptr_to_arr_after_star {
+ int (* __counted_by(count) buf)[4];
+ int count;
+};
+
+// Position: after identifier, before ) (inside parens)
+// Invalid position - causes parse error
+struct ptr_to_arr_after_ident {
+ int (*buf __counted_by(count))[4]; // Invalid position
+ // expected-error at -1{{expected ')'}}
+ // expected-note at -2{{to match this '('}}
+ int count;
+};
+
+// Position: after [4]
+// Applies to the top-level type.
+struct ptr_to_arr_after_brackets {
+ int (* buf)[4] __counted_by(count);
+ int count;
+};
+
+// Position: after (, before *
+struct ptr_to_arr_after_lparen {
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ int (__counted_by(count) *buf)[4];
+ int count;
+};
+
+// Position: inside [4]
+struct ptr_to_arr_inside_brackets {
+ int (* buf)[4 __counted_by(count)]; // Invalid syntax
+ // expected-error at -1{{expected ']'}}
+ // expected-note at -2{{to match this '['}}
+ int count;
+};
+
+// Position: before [4]
+struct ptr_to_arr_before_brackets {
+ // expected-error at +1{{expected ';' at end of declaration list}}
+ int (* buf) __counted_by(count) [4]; // Invalid syntax
+ int count;
+};
+
+// Position: double parens, after ((, before *
+struct ptr_to_arr_double_paren1 {
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ int ((__counted_by(count) * buf))[4];
+ int count;
+};
+
+// Position: double parens, after *, before identifier
+struct ptr_to_arr_double_paren2 {
+ int ((* __counted_by(count) buf))[4];
+ int count;
+};
+
+// ============================================================================
+// POINTER TO ARRAY WITH QUALIFIERS
+// ============================================================================
+
+// const pointer
+struct ptr_to_arr_const_ptr1 {
+ int (* const __counted_by(count) buf)[4];
+ int count;
+};
+
+struct ptr_to_arr_const_ptr2 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* const buf)[4];
+ int count;
+};
+
+// pointer to const
+struct ptr_to_arr_ptr_to_const {
+ const int (* __counted_by(count) buf)[4];
+ int count;
+};
+
+struct ptr_to_arr_ptr_to_const2 {
+ int const (* __counted_by(count) buf)[4];
+ int count;
+};
+
+// restrict pointer
+struct ptr_to_arr_restrict1 {
+ int (* __restrict __counted_by(count) buf)[4];
+ int count;
+};
+
+struct ptr_to_arr_restrict2 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* __restrict buf)[4];
+ int count;
+};
+
+// ============================================================================
+// POINTER TO MULTI-DIMENSIONAL ARRAY: int (*buf)[4][8]
+// ============================================================================
+
+struct ptr_to_multidim_arr_after_type {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* buf)[4][8];
+ int count;
+};
+
+struct ptr_to_multidim_arr_after_star {
+ int (* __counted_by(count) buf)[4][8];
+ int count;
+};
+
+struct ptr_to_multidim_arr_middle {
+ // expected-error at +1{{expected ';' at end of declaration list}}
+ int (* buf)[4] __counted_by(count) [8]; // Invalid position
+ int count;
+};
+
+struct ptr_to_multidim_arr_after_all {
+ int (* buf)[4][8] __counted_by(count);
+ // This doesn't trigger an error - the attribute applies to the pointer
+ int count;
+};
+
+// ============================================================================
+// ARRAY OF POINTERS TO ARRAY: int (*buf[10])[4]
+// ============================================================================
+
+struct arr_of_ptr_to_arr_after_type {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* buf[10])[4];
+ int count;
+};
+
+struct arr_of_ptr_to_arr_after_star {
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ int (* __counted_by(count) buf[10])[4];
+ int count;
+};
+
+struct arr_of_ptr_to_arr_middle {
+ // expected-error at +2{{'counted_by' on arrays only applies to C99 flexible array members}}
+ // expected-error at +1{{expected ';' at end of declaration list}}
+ int (* buf[10]) __counted_by(count) [4]; // Invalid position
+ int count;
+};
+
+struct arr_of_ptr_to_arr_inside_first_brackets {
+ int (* buf __counted_by(count) [10])[4];
+ // expected-error at -1{{expected ')'}}
+ // expected-note at -2{{to match this '('}}
+ int count;
+};
+
+// ============================================================================
+// TYPEDEF ARRAY: arr4_t *buf where arr4_t is int[4]
+// ============================================================================
+
+typedef int arr4_t[4];
+
+struct typedef_arr_before_type {
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ __counted_by(count) arr4_t * buf;
+ int count;
+};
+
+struct typedef_arr_after_type {
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ arr4_t __counted_by(count) * buf;
+ int count;
+};
+
+struct typedef_arr_after_star {
+ arr4_t * __counted_by(count) buf;
+ int count;
+};
+
+// ============================================================================
+// FUNCTION POINTER: int (*buf)(void)
+// ============================================================================
+
+// Position: after *, before identifier
+struct fptr_after_star {
+ // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'int (void)' is a function type}}
+ int (* __counted_by(count) buf)(void);
+ int count;
+};
+
+// Position: after (, before *
+struct fptr_after_lparen {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int (__counted_by(count) *buf)(void);
+ int count;
+};
+
+// ============================================================================
+// _ATOMIC POINTER VARIATIONS
+// ============================================================================
+
+// _Atomic(int *) - atomic pointer type
+struct atomic_ptr_type {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ _Atomic(int *) __counted_by(count) buf;
+ int count;
+};
+
+// Attribute inside _Atomic (likely invalid)
+struct atomic_ptr_attr_inside {
+ // expected-error at +1{{use of undeclared identifier 'count'}}
+ _Atomic(int *__counted_by(count)) buf;
+ int count;
+};
+
+struct atomic_ptr_attr_inside_no_forward_ref {
+ int count;
+ // FIXME: should not be allowed
+ _Atomic(int *__counted_by(count)) buf;
+};
+
+struct atomic_ptr_attr_after {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ __counted_by(count) _Atomic(int *) buf;
+ int count;
+};
+
+struct atomic_ptr_attr_after_no_forward_ref {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ __counted_by(count) _Atomic(int *) buf;
+};
+
+// _Atomic int * - could be atomic int or atomic pointer
+struct atomic_ambiguous {
+ _Atomic int * __counted_by(count) buf;
+ int count;
+};
+
+// int *_Atomic - atomic pointer (unambiguous)
+struct atomic_ptr_unambiguous1 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int *_Atomic __counted_by(count) buf;
+ int count;
+};
+
+// __counted_by before _Atomic
+struct atomic_ptr_attr_before_atomic1 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int *__counted_by(count) _Atomic buf;
+ int count;
+};
+
+// __counted_by before * _Atomic
+struct atomic_ptr_attr_before_atomic2 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) * _Atomic buf;
+ int count;
+};
+
+// _Atomic before type
+struct atomic_ptr_atomic_first1 {
+ _Atomic int *__counted_by(count) buf;
+ int count;
+};
+
+// _Atomic before type, attribute after *
+struct atomic_ptr_atomic_first2 {
+ _Atomic int * __counted_by(count) buf;
+ int count;
+};
+
+// __counted_by at the end
+struct atomic_ptr_attr_at_end1 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int *_Atomic buf __counted_by(count);
+ int count;
+};
+
+// __counted_by at the end with space
+struct atomic_ptr_attr_at_end2 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * _Atomic buf __counted_by(count);
+ int count;
+};
+
+// ============================================================================
+// _ATOMIC POINTER TO ARRAY
+// ============================================================================
+
+struct atomic_ptr_to_arr1 {
+ _Atomic int (* __counted_by(count) buf)[4];
+ int count;
+};
+
+struct atomic_ptr_to_arr2 {
+ // expected-error at +3{{expected a type}}
+ // expected-error at +2{{use of undeclared identifier 'count'}}
+ // expected-error at +1{{expected member name or ';' after declaration specifiers}}
+ int _Atomic (* __counted_by(count) buf)[4];
+ int count;
+};
+
+struct atomic_ptr_to_arr3 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int (* _Atomic __counted_by(count) buf)[4];
+ int count;
+};
+
+// ============================================================================
+// ATOMIC WITH CONST/VOLATILE/RESTRICT QUALIFIERS
+// ============================================================================
+
+// const _Atomic pointer
+struct atomic_const_ptr1 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * const _Atomic __counted_by(count) buf;
+ int count;
+};
+
+struct atomic_const_ptr2 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * _Atomic const __counted_by(count) buf;
+ int count;
+};
+
+struct atomic_const_ptr3 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ const int * _Atomic __counted_by(count) buf;
+ int count;
+};
+
+// volatile _Atomic pointer
+struct atomic_volatile_ptr1 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * volatile _Atomic __counted_by(count) buf;
+ int count;
+};
+
+struct atomic_volatile_ptr2 {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * _Atomic volatile __counted_by(count) buf;
+ int count;
+};
+
+// restrict _Atomic pointer
+struct atomic_restrict_ptr1 {
+ // expected-error at +2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}}
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * __restrict _Atomic __counted_by(count) buf;
+ int count;
+};
+
+struct atomic_restrict_ptr2 {
+ // expected-error at +2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}}
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * _Atomic __restrict __counted_by(count) buf;
+ int count;
+};
+
+// Combined qualifiers
+struct atomic_const_volatile_ptr {
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * const volatile _Atomic __counted_by(count) buf;
+ int count;
+};
+
+struct atomic_all_qualifiers {
+ // expected-error at +2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}}
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * const volatile __restrict _Atomic __counted_by(count) buf;
+ int count;
+};
diff --git a/clang/test/Sema/attr-counted-by-weird-type-positions.c b/clang/test/Sema/attr-counted-by-weird-type-positions.c
new file mode 100644
index 0000000000000..2668ab1e18346
--- /dev/null
+++ b/clang/test/Sema/attr-counted-by-weird-type-positions.c
@@ -0,0 +1,454 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#define __counted_by(f) __attribute__((counted_by(f)))
+
+// ============================================================================
+// SIMPLE POINTER: int *buf
+// ============================================================================
+
+// Position: after *, before identifier
+// Applies to `int *`.
+struct ptr_after_star {
+ int count;
+ int *__counted_by(count) buf;
+};
+
+// Position: before type specifier
+// Applies to the top-level type.
+struct ptr_before_type {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ __counted_by(count) int *buf;
+};
+
+// Position: after type, before *
+// Applies to `int`.
+struct ptr_after_type {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) *buf;
+};
+
+// Position: after identifier
+// Applies to the top-level type.
+struct ptr_after_ident {
+ int count;
+ int *buf __counted_by(count);
+};
+
+// ============================================================================
+// TYPEDEF POINTER: ptr_to_int_t buf
+// ============================================================================
+
+typedef int * ptr_to_int_t;
+
+// Position: after typedef name, before identifier
+// Applies to `ptr_to_int_t`.
+struct typedef_after_type {
+ int count;
+ ptr_to_int_t __counted_by(count) buf;
+};
+
+// Position: before typedef name
+// Applies to the top-level type.
+struct typedef_before_type {
+ int count;
+ __counted_by(count) ptr_to_int_t buf;
+};
+
+// Position: after identifier
+// Applies to the top-level type.
+struct typedef_after_ident {
+ int count;
+ ptr_to_int_t buf __counted_by(count);
+};
+
+// ============================================================================
+// POINTER TO ARRAY: int (*buf)[4]
+// ============================================================================
+
+// Position: after type, before (*...)
+// Applies to `int`.
+struct ptr_to_arr_after_type {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* buf)[4];
+};
+
+// Position: before type
+// Applies to the top-level type.
+struct ptr_to_arr_before_type {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ __counted_by(count) int (* buf)[4];
+};
+
+// Position: after *, before identifier (inside parens)
+// Applies to `int (*)[4]`.
+struct ptr_to_arr_after_star {
+ int count;
+ int (* __counted_by(count) buf)[4];
+};
+
+// Position: after identifier, before ) (inside parens)
+// Invalid position - causes parse error
+struct ptr_to_arr_after_ident {
+ int count;
+ int (*buf __counted_by(count))[4]; // Invalid position
+ // expected-error at -1{{expected ')'}}
+ // expected-note at -2{{to match this '('}}
+};
+
+// Position: after [4]
+// Applies to the top-level type.
+struct ptr_to_arr_after_brackets {
+ int count;
+ int (* buf)[4] __counted_by(count);
+};
+
+// Position: after (, before *
+struct ptr_to_arr_after_lparen {
+ int count;
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ int (__counted_by(count) *buf)[4];
+};
+
+// Position: inside [4]
+struct ptr_to_arr_inside_brackets {
+ int count;
+ int (* buf)[4 __counted_by(count)]; // Invalid syntax
+ // expected-error at -1{{expected ']'}}
+ // expected-note at -2{{to match this '['}}
+};
+
+// Position: before [4]
+struct ptr_to_arr_before_brackets {
+ int count;
+ // expected-error at +1{{expected ';' at end of declaration list}}
+ int (* buf) __counted_by(count) [4]; // Invalid syntax
+};
+
+// Position: double parens, after ((, before *
+struct ptr_to_arr_double_paren1 {
+ int count;
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ int ((__counted_by(count) * buf))[4];
+};
+
+// Position: double parens, after *, before identifier
+struct ptr_to_arr_double_paren2 {
+ int count;
+ int ((* __counted_by(count) buf))[4];
+};
+
+// ============================================================================
+// POINTER TO ARRAY WITH QUALIFIERS
+// ============================================================================
+
+// const pointer
+struct ptr_to_arr_const_ptr1 {
+ int count;
+ int (* const __counted_by(count) buf)[4];
+};
+
+struct ptr_to_arr_const_ptr2 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* const buf)[4];
+};
+
+// pointer to const
+struct ptr_to_arr_ptr_to_const {
+ int count;
+ const int (* __counted_by(count) buf)[4];
+};
+
+struct ptr_to_arr_ptr_to_const2 {
+ int count;
+ int const (* __counted_by(count) buf)[4];
+};
+
+// restrict pointer
+struct ptr_to_arr_restrict1 {
+ int count;
+ int (* __restrict __counted_by(count) buf)[4];
+};
+
+struct ptr_to_arr_restrict2 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* __restrict buf)[4];
+};
+
+// ============================================================================
+// POINTER TO MULTI-DIMENSIONAL ARRAY: int (*buf)[4][8]
+// ============================================================================
+
+struct ptr_to_multidim_arr_after_type {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* buf)[4][8];
+};
+
+struct ptr_to_multidim_arr_after_star {
+ int count;
+ int (* __counted_by(count) buf)[4][8];
+};
+
+struct ptr_to_multidim_arr_middle {
+ int count;
+ // expected-error at +1{{expected ';' at end of declaration list}}
+ int (* buf)[4] __counted_by(count) [8]; // Invalid position
+};
+
+struct ptr_to_multidim_arr_after_all {
+ int count;
+ int (* buf)[4][8] __counted_by(count);
+ // This doesn't trigger an error - the attribute applies to the pointer
+};
+
+// ============================================================================
+// ARRAY OF POINTERS TO ARRAY: int (*buf[10])[4]
+// ============================================================================
+
+struct arr_of_ptr_to_arr_after_type {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) (* buf[10])[4];
+};
+
+struct arr_of_ptr_to_arr_after_star {
+ int count;
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ int (* __counted_by(count) buf[10])[4];
+};
+
+struct arr_of_ptr_to_arr_middle {
+ int count;
+ // expected-error at +2{{'counted_by' on arrays only applies to C99 flexible array members}}
+ // expected-error at +1{{expected ';' at end of declaration list}}
+ int (* buf[10]) __counted_by(count) [4]; // Invalid position
+};
+
+struct arr_of_ptr_to_arr_inside_first_brackets {
+ int count;
+ int (* buf __counted_by(count) [10])[4];
+ // expected-error at -1{{expected ')'}}
+ // expected-note at -2{{to match this '('}}
+};
+
+// ============================================================================
+// TYPEDEF ARRAY: arr4_t *buf where arr4_t is int[4]
+// ============================================================================
+
+typedef int arr4_t[4];
+
+struct typedef_arr_before_type {
+ int count;
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ __counted_by(count) arr4_t * buf;
+};
+
+struct typedef_arr_after_type {
+ int count;
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
+ arr4_t __counted_by(count) * buf;
+};
+
+struct typedef_arr_after_star {
+ int count;
+ arr4_t * __counted_by(count) buf;
+};
+
+// ============================================================================
+// FUNCTION POINTER: int (*buf)(void)
+// ============================================================================
+
+// Position: after *, before identifier
+struct fptr_after_star {
+ int count;
+ // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'int (void)' is a function type}}
+ int (* __counted_by(count) buf)(void);
+};
+
+// Position: after (, before *
+struct fptr_after_lparen {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int (__counted_by(count) *buf)(void);
+};
+
+// ============================================================================
+// _ATOMIC POINTER VARIATIONS
+// ============================================================================
+
+// _Atomic(int *) - atomic pointer type
+struct atomic_ptr_type {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ _Atomic(int *) __counted_by(count) buf;
+};
+
+// Attribute inside _Atomic (likely invalid)
+struct atomic_ptr_attr_inside {
+ int count;
+ _Atomic(int *__counted_by(count)) buf;
+};
+
+struct atomic_ptr_attr_inside_no_forward_ref {
+ int count;
+ // FIXME: should not be allowed
+ _Atomic(int *__counted_by(count)) buf;
+};
+
+struct atomic_ptr_attr_after {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ __counted_by(count) _Atomic(int *) buf;
+};
+
+struct atomic_ptr_attr_after_no_forward_ref {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ __counted_by(count) _Atomic(int *) buf;
+};
+
+// _Atomic int * - could be atomic int or atomic pointer
+struct atomic_ambiguous {
+ int count;
+ _Atomic int * __counted_by(count) buf;
+};
+
+// int *_Atomic - atomic pointer (unambiguous)
+struct atomic_ptr_unambiguous1 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int *_Atomic __counted_by(count) buf;
+};
+
+// __counted_by before _Atomic
+struct atomic_ptr_attr_before_atomic1 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int *__counted_by(count) _Atomic buf;
+};
+
+// __counted_by before * _Atomic
+struct atomic_ptr_attr_before_atomic2 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int __counted_by(count) * _Atomic buf;
+};
+
+// _Atomic before type
+struct atomic_ptr_atomic_first1 {
+ int count;
+ _Atomic int *__counted_by(count) buf;
+};
+
+// _Atomic before type, attribute after *
+struct atomic_ptr_atomic_first2 {
+ int count;
+ _Atomic int * __counted_by(count) buf;
+};
+
+// __counted_by at the end
+struct atomic_ptr_attr_at_end1 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int *_Atomic buf __counted_by(count);
+};
+
+// __counted_by at the end with space
+struct atomic_ptr_attr_at_end2 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * _Atomic buf __counted_by(count);
+};
+
+// ============================================================================
+// _ATOMIC POINTER TO ARRAY
+// ============================================================================
+
+struct atomic_ptr_to_arr1 {
+ int count;
+ _Atomic int (* __counted_by(count) buf)[4];
+};
+
+struct atomic_ptr_to_arr2 {
+ int count;
+ // expected-error at +2{{expected a type}}
+ // expected-error at +1{{expected member name or ';' after declaration specifiers}}
+ int _Atomic (* __counted_by(count) buf)[4];
+};
+
+struct atomic_ptr_to_arr3 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int (* _Atomic __counted_by(count) buf)[4];
+};
+
+// ============================================================================
+// ATOMIC WITH CONST/VOLATILE/RESTRICT QUALIFIERS
+// ============================================================================
+
+// const _Atomic pointer
+struct atomic_const_ptr1 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * const _Atomic __counted_by(count) buf;
+};
+
+struct atomic_const_ptr2 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * _Atomic const __counted_by(count) buf;
+};
+
+struct atomic_const_ptr3 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ const int * _Atomic __counted_by(count) buf;
+};
+
+// volatile _Atomic pointer
+struct atomic_volatile_ptr1 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * volatile _Atomic __counted_by(count) buf;
+};
+
+struct atomic_volatile_ptr2 {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * _Atomic volatile __counted_by(count) buf;
+};
+
+// restrict _Atomic pointer
+struct atomic_restrict_ptr1 {
+ int count;
+ // expected-error at +2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}}
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * __restrict _Atomic __counted_by(count) buf;
+};
+
+struct atomic_restrict_ptr2 {
+ int count;
+ // expected-error at +2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}}
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * _Atomic __restrict __counted_by(count) buf;
+};
+
+// Combined qualifiers
+struct atomic_const_volatile_ptr {
+ int count;
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * const volatile _Atomic __counted_by(count) buf;
+};
+
+struct atomic_all_qualifiers {
+ int count;
+ // expected-error at +2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}}
+ // expected-error at +1{{'counted_by' only applies to pointers or C99 flexible array members}}
+ int * const volatile __restrict _Atomic __counted_by(count) buf;
+};
>From e785278900fcc01e606caef4b5266538d6c2f477 Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Sat, 7 Feb 2026 09:17:17 -0800
Subject: [PATCH 03/10] Skip rebuilding RecordType for late parsing; Add
LateParsedAttrType printer
---
clang/lib/AST/TypePrinter.cpp | 15 +++++++++++++--
clang/lib/Sema/SemaDecl.cpp | 12 ++++++++++--
2 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 3ac50f486de56..7f8e4f0a51cfc 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -37,6 +37,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
+#include "clang/Sema/DeclSpec.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SaveAndRestore.h"
@@ -1852,18 +1853,28 @@ void TypePrinter::printCountAttributedAfter(const CountAttributedType *T,
printCountAttributedImpl(T, OS, Policy);
}
+static void printLateParsedAttrImpl(const LateParsedAttrType *T,
+ raw_ostream &OS) {
+ OS << " LateParsedTypeAttr::" << T->getLateParsedAttribute()->AttrName.getName() << "()";
+}
+
void TypePrinter::printLateParsedAttrBefore(const LateParsedAttrType *T,
raw_ostream &OS) {
// LateParsedAttrType is a transient placeholder that should not appear
- // in user-facing output. Just print the wrapped type.
+ // in final output. Print it explicitly for debugging purposes.
printBefore(T->getWrappedType(), OS);
+ if (!T->isArrayType())
+ printLateParsedAttrImpl(T, OS);
}
void TypePrinter::printLateParsedAttrAfter(const LateParsedAttrType *T,
raw_ostream &OS) {
// LateParsedAttrType is a transient placeholder that should not appear
- // in user-facing output. Just print the wrapped type.
+ // in final output. The attribute name is already printed in printBefore
+ // for pointer types, but for array types we print it here.
printAfter(T->getWrappedType(), OS);
+ if (T->isArrayType())
+ printLateParsedAttrImpl(T, OS);
}
void TypePrinter::printAttributedBefore(const AttributedType *T,
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9bd30ecfd7feb..8830fd0061315 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -19998,11 +19998,19 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
if (getLangOpts().ExperimentalLateParseAttributes) {
for (ArrayRef<Decl *>::iterator i = Fields.begin(), end = Fields.end();
i != end; ++i) {
- FieldDecl *FD = cast<FieldDecl>(*i);
+ // We only process FieldDecl here, not IndirectFieldDecl, because
+ // ActOnFields is called on the struct that directly contains the field.
+ // TODO: To support counted_by referring to a field in an anonymous struct
+ // declared later, late parsing should be triggered from the innermost
+ // named parent struct.
+ FieldDecl *FD = dyn_cast<FieldDecl>(*i);
+ if (FD->getType()->isRecordType())
+ continue;
RebuildTypeWithLateParsedAttr RebuildFieldType(*this, FD);
+ auto *OldTSI = FD->getTypeSourceInfo();
auto *TSI = RebuildFieldType.TransformType(FD->getTypeSourceInfo());
- if (TSI) {
+ if (TSI && TSI != OldTSI) {
FD->setTypeSourceInfo(TSI);
FD->setType(TSI->getType());
}
>From f36d41b906bd392b9502e30fbe7918545790fd06 Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Sat, 7 Feb 2026 09:18:05 -0800
Subject: [PATCH 04/10] clang-format
---
clang/include/clang/AST/ASTContext.h | 2 +-
clang/include/clang/Parse/Parser.h | 7 +-
clang/include/clang/Sema/DeclSpec.h | 205 +++++++++++----------
clang/include/clang/Sema/Sema.h | 6 +-
clang/lib/AST/ASTContext.cpp | 5 +-
clang/lib/AST/ASTStructuralEquivalence.cpp | 6 +-
clang/lib/AST/TypePrinter.cpp | 9 +-
clang/lib/Parse/ParseDecl.cpp | 44 +++--
clang/lib/Sema/Sema.cpp | 3 +-
clang/lib/Sema/SemaBoundsSafety.cpp | 2 +-
clang/lib/Sema/SemaDecl.cpp | 37 ++--
clang/lib/Sema/SemaDeclAttr.cpp | 6 +-
clang/lib/Sema/SemaType.cpp | 53 +++---
clang/lib/Sema/TreeTransform.h | 5 +-
clang/lib/Serialization/ASTWriter.cpp | 3 +-
15 files changed, 206 insertions(+), 187 deletions(-)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index ac70c99d855c1..cbd70dbc4edab 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1589,7 +1589,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// This type wraps another type and holds the LateParsedAttribute
/// that will be parsed later.
QualType getLateParsedAttrType(QualType Wrapped,
- LateParsedTypeAttribute *LateParsedAttr) const;
+ LateParsedTypeAttribute *LateParsedAttr) const;
/// Return the uniqued reference to a type adjusted from the original
/// type to a new type.
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 560eeca13623e..d5c18ab9a045f 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1426,10 +1426,11 @@ class Parser : public CodeCompletionHandler {
void ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
ParsedAttributes *OutAttrs = nullptr);
+ void ParseLexedTypeAttribute(LateParsedTypeAttribute &LA, bool EnterScope,
+ ParsedAttributes &OutAttrs);
- void ParseLexedTypeAttribute(LateParsedTypeAttribute &LA, bool EnterScope, ParsedAttributes &OutAttrs);
-
- static void LateTypeAttrParserCallback(void *P, void *LA, bool EnterScope, ParsedAttributes &OutAttrs);
+ static void LateTypeAttrParserCallback(void *P, void *LA, bool EnterScope,
+ ParsedAttributes &OutAttrs);
void ParseLexedPragmas(ParsingClass &Class);
void ParseLexedPragma(LateParsedPragma &LP);
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index a44757664a58d..18b4f729d07b6 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -74,113 +74,112 @@ namespace clang {
virtual void ParseLexedPragmas();
};
-/// Contains the lexed tokens of an attribute with arguments that
-/// may reference member variables and so need to be parsed at the
-/// end of the class declaration after parsing all other member
-/// member declarations.
-/// FIXME: Perhaps we should change the name of LateParsedDeclaration to
-/// LateParsedTokens.
-struct LateParsedAttribute : public LateParsedDeclaration {
-
- enum LPA_Kind {
- LPA_Declaration,
- LPA_Type,
- };
+ /// Contains the lexed tokens of an attribute with arguments that
+ /// may reference member variables and so need to be parsed at the
+ /// end of the class declaration after parsing all other member
+ /// member declarations.
+ /// FIXME: Perhaps we should change the name of LateParsedDeclaration to
+ /// LateParsedTokens.
+ struct LateParsedAttribute : public LateParsedDeclaration {
+
+ enum LPA_Kind {
+ LPA_Declaration,
+ LPA_Type,
+ };
- Parser *Self;
- CachedTokens Toks;
- IdentifierInfo &AttrName;
- IdentifierInfo *MacroII = nullptr;
- SourceLocation AttrNameLoc;
- SmallVector<Decl *, 2> Decls;
+ Parser *Self;
+ CachedTokens Toks;
+ IdentifierInfo &AttrName;
+ IdentifierInfo *MacroII = nullptr;
+ SourceLocation AttrNameLoc;
+ SmallVector<Decl *, 2> Decls;
-private:
- LPA_Kind Kind;
+ private:
+ LPA_Kind Kind;
-public:
- explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
- SourceLocation Loc,
- LPA_Kind Kind = LPA_Declaration)
- : Self(P), AttrName(Name), AttrNameLoc(Loc), Kind(Kind) {}
+ public:
+ explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
+ SourceLocation Loc,
+ LPA_Kind Kind = LPA_Declaration)
+ : Self(P), AttrName(Name), AttrNameLoc(Loc), Kind(Kind) {}
- void ParseLexedAttributes() override;
+ void ParseLexedAttributes() override;
- void addDecl(Decl *D) { Decls.push_back(D); }
+ void addDecl(Decl *D) { Decls.push_back(D); }
- LPA_Kind getKind() const { return Kind; }
+ LPA_Kind getKind() const { return Kind; }
- // LLVM-style RTTI support
- static bool classof(const LateParsedAttribute* LA) {
+ // LLVM-style RTTI support
+ static bool classof(const LateParsedAttribute *LA) {
// LateParsedAttribute matches both Declaration and Type kinds
return LA->getKind() == LPA_Declaration || LA->getKind() == LPA_Type;
- }
-};
+ }
+ };
-/// Contains the lexed tokens of an attribute with arguments that
-/// may reference member variables and so need to be parsed at the
-/// end of the class declaration after parsing all other member
-/// member declarations.
-/// FIXME: Perhaps we should change the name of LateParsedDeclaration to
-/// LateParsedTokens.
-struct LateParsedTypeAttribute : public LateParsedAttribute {
+ /// Contains the lexed tokens of an attribute with arguments that
+ /// may reference member variables and so need to be parsed at the
+ /// end of the class declaration after parsing all other member
+ /// member declarations.
+ /// FIXME: Perhaps we should change the name of LateParsedDeclaration to
+ /// LateParsedTokens.
+ struct LateParsedTypeAttribute : public LateParsedAttribute {
- explicit LateParsedTypeAttribute(Parser *P, IdentifierInfo &Name,
- SourceLocation Loc)
- : LateParsedAttribute(P, Name, Loc, LPA_Type) {}
+ explicit LateParsedTypeAttribute(Parser *P, IdentifierInfo &Name,
+ SourceLocation Loc)
+ : LateParsedAttribute(P, Name, Loc, LPA_Type) {}
- void ParseLexedAttributes() override;
- void ParseLexedTypeAttributes() override;
+ void ParseLexedAttributes() override;
+ void ParseLexedTypeAttributes() override;
- void addDecl(Decl *D) { Decls.push_back(D); }
+ void addDecl(Decl *D) { Decls.push_back(D); }
- /// Parse this late-parsed type attribute and store results in OutAttrs.
- /// This method can be called from Sema during type transformation to
- /// parse the cached tokens and produce the final attribute.
- void ParseInto(ParsedAttributes &OutAttrs);
+ /// Parse this late-parsed type attribute and store results in OutAttrs.
+ /// This method can be called from Sema during type transformation to
+ /// parse the cached tokens and produce the final attribute.
+ void ParseInto(ParsedAttributes &OutAttrs);
- // LLVM-style RTTI support
- static bool classof(const LateParsedAttribute* LA) {
+ // LLVM-style RTTI support
+ static bool classof(const LateParsedAttribute *LA) {
return LA->getKind() == LPA_Type;
- }
-};
-
-// 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() { return ParseSoon; }
- /// returns true iff the attribute to be parsed should only be late parsed
- /// if it is annotated with `LateAttrParseExperimentalExt`
- bool lateAttrParseExperimentalExtOnly() {
- return LateAttrParseExperimentalExtOnly;
- }
+ }
+ };
- bool lateAttrParseTypeAttrOnly() {
- return LateAttrParseTypeAttrOnly;
- }
+ // 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() { return ParseSoon; }
+ /// returns true iff the attribute to be parsed should only be late parsed
+ /// if it is annotated with `LateAttrParseExperimentalExt`
+ bool lateAttrParseExperimentalExtOnly() {
+ return LateAttrParseExperimentalExtOnly;
+ }
- void takeTypeAttrsAppendingFrom(LateParsedAttrList &Other) {
- auto it = std::remove_if(Other.begin(), Other.end(), [&](LateParsedAttribute *LA){
- if (auto *LTA = dyn_cast<LateParsedTypeAttribute>(LA)) {
- push_back(LTA);
- return true;
- }
- return false;
- });
- Other.erase(it, Other.end());
- }
+ bool lateAttrParseTypeAttrOnly() { return LateAttrParseTypeAttrOnly; }
+
+ void takeTypeAttrsAppendingFrom(LateParsedAttrList &Other) {
+ auto it = std::remove_if(
+ Other.begin(), Other.end(), [&](LateParsedAttribute *LA) {
+ if (auto *LTA = dyn_cast<LateParsedTypeAttribute>(LA)) {
+ push_back(LTA);
+ return true;
+ }
+ return false;
+ });
+ Other.erase(it, Other.end());
+ }
-private:
- bool ParseSoon; // Are we planning to parse these shortly after creation?
- bool LateAttrParseExperimentalExtOnly;
- bool LateAttrParseTypeAttrOnly;
-};
+ private:
+ bool ParseSoon; // Are we planning to parse these shortly after creation?
+ bool LateAttrParseExperimentalExtOnly;
+ bool LateAttrParseTypeAttrOnly;
+ };
/// Represents a C++ nested-name-specifier or a global scope specifier.
///
@@ -600,7 +599,8 @@ class DeclSpec {
FS_noreturn_specified(false), FriendSpecifiedFirst(false),
ConstexprSpecifier(
static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
- Attrs(attrFactory), LateParsedAttrs(true, true, true), writtenBS(), ObjCQualifiers(nullptr) {}
+ Attrs(attrFactory), LateParsedAttrs(true, true, true), writtenBS(),
+ ObjCQualifiers(nullptr) {}
// storage-class-specifier
SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; }
@@ -978,9 +978,11 @@ class DeclSpec {
ParsedAttributes &getAttributes() { return Attrs; }
const ParsedAttributes &getAttributes() const { return Attrs; }
- LateParsedAttrList *getLateAttributePtr() { return &LateParsedAttrs; }
- LateParsedAttrList &getLateAttributes() { return LateParsedAttrs; }
- const LateParsedAttrList &getLateAttributes() const { return LateParsedAttrs; }
+ LateParsedAttrList *getLateAttributePtr() { return &LateParsedAttrs; }
+ LateParsedAttrList &getLateAttributes() { return LateParsedAttrs; }
+ const LateParsedAttrList &getLateAttributes() const {
+ return LateParsedAttrs;
+ }
void takeAttributesAppendingingFrom(ParsedAttributes &attrs) {
Attrs.takeAllAppendingFrom(attrs);
@@ -1359,7 +1361,7 @@ class UnqualifiedId {
///
/// This is intended to be a small value object.
struct DeclaratorChunk {
- DeclaratorChunk() : LateAttrList(true, true, true) {};
+ DeclaratorChunk() : LateAttrList(true, true, true){};
enum {
Pointer, Reference, Array, Function, BlockPointer, MemberPointer, Paren, Pipe
@@ -2145,8 +2147,8 @@ class Declarator {
Redeclaration(false), Extension(false), ObjCIvar(false),
ObjCWeakProperty(false), InlineStorageUsed(false),
HasInitializer(false), Attrs(DS.getAttributePool().getFactory()),
- DeclarationAttrs(DeclarationAttrs), LateParsedAttrs(true, true, true), AsmLabel(nullptr),
- TrailingRequiresClause(nullptr),
+ DeclarationAttrs(DeclarationAttrs), LateParsedAttrs(true, true, true),
+ AsmLabel(nullptr), TrailingRequiresClause(nullptr),
InventedTemplateParameterList(nullptr) {
assert(llvm::all_of(DeclarationAttrs,
[](const ParsedAttr &AL) {
@@ -2467,10 +2469,9 @@ class Declarator {
/// EndLoc, which should be the last token of the chunk.
/// 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,
- const LateParsedAttrList &LateAttrs = {}) {
+ void AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
+ SourceLocation EndLoc,
+ const LateParsedAttrList &LateAttrs = {}) {
DeclTypeInfo.push_back(TI);
DeclTypeInfo.back().getAttrs().prepend(attrs.begin(), attrs.end());
getAttributePool().takeAllFrom(attrs.getPool());
@@ -2808,8 +2809,10 @@ class Declarator {
return DeclarationAttrs;
}
- LateParsedAttrList &getLateAttributes() { return LateParsedAttrs; }
- const LateParsedAttrList &getLateAttributes() const { return LateParsedAttrs; }
+ LateParsedAttrList &getLateAttributes() { return LateParsedAttrs; }
+ const LateParsedAttrList &getLateAttributes() const {
+ return LateParsedAttrs;
+ }
void takeLateTypeAttributesAppending(LateParsedAttrList &lateAttrs) {
LateParsedAttrs.takeTypeAttrsAppendingFrom(lateAttrs);
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 0bde208c2ac8c..b8f3e12382052 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2457,11 +2457,11 @@ class Sema final : public SemaBase {
/// `counted_by_or_null` attribute.
///
/// \returns false iff semantically valid.
- bool CheckCountedByAttrOnField(FieldDecl *FD, QualType T, Expr *E, bool CountInBytes,
- bool OrNull);
+ bool CheckCountedByAttrOnField(FieldDecl *FD, QualType T, Expr *E,
+ bool CountInBytes, bool OrNull);
bool CheckCountedByAttrOnFieldDecl(FieldDecl *FD, Expr *E, bool CountInBytes,
- bool OrNull);
+ bool OrNull);
/// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
/// `__counted_by_or_null` pointer type \param LHSTy.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 07e2d0d3cd943..d2fff816a0371 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -49,7 +49,6 @@
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/Builtins.h"
-#include "clang/Sema/DeclSpec.h"
#include "clang/Basic/CommentOptions.h"
#include "clang/Basic/ExceptionSpecificationType.h"
#include "clang/Basic/IdentifierTable.h"
@@ -66,6 +65,7 @@
#include "clang/Basic/TargetCXXABI.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/XRayLists.h"
+#include "clang/Sema/DeclSpec.h"
#include "llvm/ADT/APFixedPoint.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APSInt.h"
@@ -2469,7 +2469,8 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
return getTypeInfo(cast<CountAttributedType>(T)->desugar().getTypePtr());
case Type::LateParsedAttr:
- return getTypeInfo(cast<LateParsedAttrType>(T)->getWrappedType().getTypePtr());
+ return getTypeInfo(
+ cast<LateParsedAttrType>(T)->getWrappedType().getTypePtr());
case Type::BTFTagAttributed:
return getTypeInfo(
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 58a968d1c54a9..611e10d329cb9 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1149,9 +1149,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
break;
case Type::LateParsedAttr:
- if (!IsStructurallyEquivalent(Context,
- cast<LateParsedAttrType>(T1)->getWrappedType(),
- cast<LateParsedAttrType>(T2)->getWrappedType()))
+ if (!IsStructurallyEquivalent(
+ Context, cast<LateParsedAttrType>(T1)->getWrappedType(),
+ cast<LateParsedAttrType>(T2)->getWrappedType()))
return false;
break;
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 7f8e4f0a51cfc..f7fe991e6f9f6 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -32,12 +32,12 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
+#include "clang/Sema/DeclSpec.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
-#include "clang/Sema/DeclSpec.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SaveAndRestore.h"
@@ -1855,11 +1855,12 @@ void TypePrinter::printCountAttributedAfter(const CountAttributedType *T,
static void printLateParsedAttrImpl(const LateParsedAttrType *T,
raw_ostream &OS) {
- OS << " LateParsedTypeAttr::" << T->getLateParsedAttribute()->AttrName.getName() << "()";
+ OS << " LateParsedTypeAttr::"
+ << T->getLateParsedAttribute()->AttrName.getName() << "()";
}
void TypePrinter::printLateParsedAttrBefore(const LateParsedAttrType *T,
- raw_ostream &OS) {
+ raw_ostream &OS) {
// LateParsedAttrType is a transient placeholder that should not appear
// in final output. Print it explicitly for debugging purposes.
printBefore(T->getWrappedType(), OS);
@@ -1868,7 +1869,7 @@ void TypePrinter::printLateParsedAttrBefore(const LateParsedAttrType *T,
}
void TypePrinter::printLateParsedAttrAfter(const LateParsedAttrType *T,
- raw_ostream &OS) {
+ raw_ostream &OS) {
// LateParsedAttrType is a transient placeholder that should not appear
// in final output. The attribute name is already printed in printBefore
// for pointer types, but for array types we print it here.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 99ce51895be72..14b7eda4b8dc3 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -116,7 +116,8 @@ static bool IsAttributeTypeAttr(ParsedAttr::Kind Kind) {
#define TYPE_ATTR(NAME) case ParsedAttr::AT_##NAME:
#include "clang/Basic/AttrList.inc"
return true;
- default: return false;
+ default:
+ return false;
#undef DECL_OR_TYPE_ATTR
#undef TYPE_ATTR
#undef ATTR
@@ -172,9 +173,8 @@ bool Parser::ParseSingleGNUAttribute(ParsedAttributes &Attrs,
return false;
}
-
- ParsedAttr::Kind AttrKind =
- ParsedAttr::getParsedKind(AttrName, nullptr, ParsedAttr::Form::GNU().getSyntax());
+ ParsedAttr::Kind AttrKind = ParsedAttr::getParsedKind(
+ AttrName, nullptr, ParsedAttr::Form::GNU().getSyntax());
bool LateParse = false;
if (!LateAttrs)
@@ -185,7 +185,8 @@ bool Parser::ParseSingleGNUAttribute(ParsedAttributes &Attrs,
// only be late parsed if the experimental language option is enabled.
LateParse = getLangOpts().ExperimentalLateParseAttributes &&
IsAttributeLateParsedExperimentalExt(*AttrName) &&
- (IsAttributeTypeAttr(AttrKind) || !LateAttrs->lateAttrParseTypeAttrOnly());
+ (IsAttributeTypeAttr(AttrKind) ||
+ !LateAttrs->lateAttrParseTypeAttrOnly());
} else {
// The caller did not restrict late parsing to only
// `LateAttrParseExperimentalExt` attributes so late parse
@@ -2755,9 +2756,11 @@ void Parser::ParseSpecifierQualifierList(
ParsedTemplateInfo TemplateInfo;
if (LateAttrs)
- assert(!std::any_of(LateAttrs->begin(), LateAttrs->end(), [&](const LateParsedAttribute *LA) {
- return isa<LateParsedTypeAttribute>(LA);
- }) && "Late type attribute carried over");
+ 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.
@@ -4824,7 +4827,9 @@ void Parser::ParseLexedCAttributeList(LateParsedAttrList &LAs, bool EnterScope,
LAs.clear();
}
-void Parser::ParseLexedTypeAttribute(LateParsedTypeAttribute &LA, bool EnterScope, ParsedAttributes &OutAttrs) {
+void Parser::ParseLexedTypeAttribute(LateParsedTypeAttribute &LA,
+ bool EnterScope,
+ ParsedAttributes &OutAttrs) {
// Create a fake EOF so that attribute parsing won't go off the end of the
// attribute.
Token AttrEnd;
@@ -4926,10 +4931,12 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
}
}
-void Parser::LateTypeAttrParserCallback(void *P, void *OLA, bool EnterScope, ParsedAttributes &OutAttrs) {
+void Parser::LateTypeAttrParserCallback(void *P, void *OLA, bool EnterScope,
+ ParsedAttributes &OutAttrs) {
auto *LA = static_cast<LateParsedAttribute *>(OLA);
auto *LTA = cast<LateParsedTypeAttribute>(LA);
- return ((Parser *)(P))->ParseLexedTypeAttribute(*LTA, /*EnterScope=*/false, OutAttrs);
+ return ((Parser *)(P))
+ ->ParseLexedTypeAttribute(*LTA, /*EnterScope=*/false, OutAttrs);
}
void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
@@ -6553,15 +6560,13 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
: AR_GNUAttributesParsedAndRejected);
// FIXME: Don't need to pass parameter. It's not used. This is the path
// where it is experimental only.
- // FIXME: Still don't know whether this is the right context to do late parsing. Is it okay?
- // You don't want to do late parsing if it's a variable declaration.
- // You can probably look at the DeclaratorContext!
+ // FIXME: Still don't know whether this is the right context to do late
+ // parsing. Is it okay? You don't want to do late parsing if it's a variable
+ // declaration. You can probably look at the DeclaratorContext!
bool LateParsingContext = D.getContext() == DeclaratorContext::Member ||
- D.getContext() == DeclaratorContext::Prototype;
+ D.getContext() == DeclaratorContext::Prototype;
LateParsedAttrList *LateAttrs =
- LateParsingContext
- ? &DS.getLateAttributes()
- : nullptr;
+ LateParsingContext ? &DS.getLateAttributes() : nullptr;
ParseTypeQualifierListOpt(DS, Reqs, /*AtomicOrPtrauthAllowed=*/true,
!D.mayOmitIdentifier(), {}, LateAttrs);
@@ -7219,7 +7224,8 @@ void Parser::ParseParenDeclarator(Declarator &D) {
return;
}
- assert(LateAttrs.empty() && "Late parsed type attribute on FirstParamAttr is dropped");
+ assert(LateAttrs.empty() &&
+ "Late parsed type attribute on FirstParamAttr is dropped");
// Okay, if this wasn't a grouping paren, it must be the start of a function
// argument list. Recognize that this declarator will never have an
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 7c51711684084..6eea8f6e9d97e 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -276,8 +276,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()),
SourceMgr(PP.getSourceManager()), APINotes(SourceMgr, LangOpts),
AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr),
- LateTemplateParser(nullptr), OpaqueParser(nullptr),
- CurContext(nullptr),
+ LateTemplateParser(nullptr), OpaqueParser(nullptr), CurContext(nullptr),
ExternalSource(nullptr), StackHandler(Diags), CurScope(nullptr),
Ident_super(nullptr), AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
ARMPtr(std::make_unique<SemaARM>(*this)),
diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp
index 7b39ba93b0aac..51f8b619efe9d 100644
--- a/clang/lib/Sema/SemaBoundsSafety.cpp
+++ b/clang/lib/Sema/SemaBoundsSafety.cpp
@@ -232,7 +232,7 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, QualType T, Expr *E,
}
bool Sema::CheckCountedByAttrOnFieldDecl(FieldDecl *FD, Expr *E,
- bool CountInBytes, bool OrNull) {
+ bool CountInBytes, bool OrNull) {
unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
if (FD->getParent()->isUnion()) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8830fd0061315..25019314a394e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10,8 +10,8 @@
//
//===----------------------------------------------------------------------===//
-#include "TypeLocBuilder.h"
#include "TreeTransform.h"
+#include "TypeLocBuilder.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTLambda.h"
@@ -19663,7 +19663,8 @@ bool Sema::EntirelyFunctionPointers(const RecordDecl *Record) {
return llvm::all_of(Record->decls(), IsFunctionPointerOrForwardDecl);
}
-static QualType handleCountedByAttrField(Sema &S, QualType T, Decl *D, const ParsedAttr &AL) {
+static QualType handleCountedByAttrField(Sema &S, QualType T, Decl *D,
+ const ParsedAttr &AL) {
if (!AL.diagnoseLangOpts(S))
return QualType();
@@ -19697,11 +19698,11 @@ static QualType handleCountedByAttrField(Sema &S, QualType T, Decl *D, const Par
llvm_unreachable("unexpected counted_by family attribute");
}
- return S.BuildCountAttributedArrayOrPointerType(
- T, CountExpr, CountInBytes, OrNull);
+ return S.BuildCountAttributedArrayOrPointerType(T, CountExpr, CountInBytes,
+ OrNull);
}
struct RebuildTypeWithLateParsedAttr
- : TreeTransform<RebuildTypeWithLateParsedAttr> {
+ : TreeTransform<RebuildTypeWithLateParsedAttr> {
FieldDecl *FD;
RebuildTypeWithLateParsedAttr(Sema &SemaRef, FieldDecl *FD)
@@ -19719,7 +19720,7 @@ struct RebuildTypeWithLateParsedAttr
}
QualType TransformLateParsedAttrType(TypeLocBuilder &TLB,
- LateParsedAttrTypeLoc TL) {
+ LateParsedAttrTypeLoc TL) {
const LateParsedAttrType *LPA = TL.getTypePtr();
auto *LTA = LPA->getLateParsedAttribute();
@@ -19788,7 +19789,7 @@ struct RebuildTypeWithLateParsedAttr
}
QualType TransformConstantArrayType(TypeLocBuilder &TLB,
- ConstantArrayTypeLoc TL) {
+ ConstantArrayTypeLoc TL) {
const ConstantArrayType *T = TL.getTypePtr();
QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());
if (ElementType.isNull()) {
@@ -19834,7 +19835,7 @@ struct RebuildTypeWithLateParsedAttr
}
QualType TransformIncompleteArrayType(TypeLocBuilder &TLB,
- IncompleteArrayTypeLoc TL) {
+ IncompleteArrayTypeLoc TL) {
const IncompleteArrayType *T = TL.getTypePtr();
QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());
if (ElementType.isNull()) {
@@ -19842,8 +19843,8 @@ struct RebuildTypeWithLateParsedAttr
return QualType();
}
- // Diagnose flexible array member with element type having counted_by attribute
- // e.g., int * __counted_by(n) arr[];
+ // Diagnose flexible array member with element type having counted_by
+ // attribute e.g., int * __counted_by(n) arr[];
if (diagnoseCountAttributedType(ElementType, TL.getLBracketLoc()))
return QualType();
@@ -19867,7 +19868,7 @@ struct RebuildTypeWithLateParsedAttr
}
QualType TransformVariableArrayType(TypeLocBuilder &TLB,
- VariableArrayTypeLoc TL) {
+ VariableArrayTypeLoc TL) {
const VariableArrayType *T = TL.getTypePtr();
QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());
if (ElementType.isNull()) {
@@ -19909,7 +19910,7 @@ struct RebuildTypeWithLateParsedAttr
}
QualType TransformDependentSizedArrayType(TypeLocBuilder &TLB,
- DependentSizedArrayTypeLoc TL) {
+ DependentSizedArrayTypeLoc TL) {
const DependentSizedArrayType *T = TL.getTypePtr();
QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());
if (ElementType.isNull()) {
@@ -19917,8 +19918,9 @@ struct RebuildTypeWithLateParsedAttr
return QualType();
}
- // Diagnose dependent-sized array with element type having counted_by attribute
- // e.g., template<int N> struct S { int * __counted_by(n) arr[N]; };
+ // Diagnose dependent-sized array with element type having counted_by
+ // attribute e.g., template<int N> struct S { int * __counted_by(n) arr[N];
+ // };
if (diagnoseCountAttributedType(ElementType, TL.getLBracketLoc()))
return QualType();
@@ -19944,7 +19946,8 @@ struct RebuildTypeWithLateParsedAttr
}
}
- DependentSizedArrayTypeLoc NewTL = TLB.push<DependentSizedArrayTypeLoc>(Result);
+ DependentSizedArrayTypeLoc NewTL =
+ TLB.push<DependentSizedArrayTypeLoc>(Result);
NewTL.setLBracketLoc(TL.getLBracketLoc());
NewTL.setRBracketLoc(TL.getRBracketLoc());
NewTL.setSizeExpr(Size);
@@ -20005,7 +20008,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
// named parent struct.
FieldDecl *FD = dyn_cast<FieldDecl>(*i);
if (FD->getType()->isRecordType())
- continue;
+ continue;
RebuildTypeWithLateParsedAttr RebuildFieldType(*this, FD);
auto *OldTSI = FD->getTypeSourceInfo();
@@ -20023,7 +20026,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
FieldDecl *FD = cast<FieldDecl>(*i);
if (auto *CAT = FD->getType()->getAs<CountAttributedType>()) {
CheckCountedByAttrOnFieldDecl(FD, CAT->getCountExpr(),
- CAT->isCountInBytes(), CAT->isOrNull());
+ CAT->isCountInBytes(), CAT->isOrNull());
}
}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index ebe877d59a55e..624b2244afa99 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7988,9 +7988,9 @@ static bool isKernelDecl(Decl *D) {
D->hasAttr<CUDAGlobalAttr>();
}
-void Sema::ProcessDeclAttributeList(Scope *S, Decl *D,
- const ParsedAttributesView &AttrList,
- const ProcessDeclAttributeOptions &Options) {
+void Sema::ProcessDeclAttributeList(
+ Scope *S, Decl *D, const ParsedAttributesView &AttrList,
+ const ProcessDeclAttributeOptions &Options) {
if (AttrList.empty())
return;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d0eb7eae44818..5d5ea63fe25e2 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -398,7 +398,9 @@ processTypeAttrs(TypeProcessingState &state, QualType &type,
TypeAttrLocation TAL, const ParsedAttributesView &attrs,
CUDAFunctionTarget CFT = CUDAFunctionTarget::HostDevice);
-static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type, const LateParsedAttrList &LateAttrs, unsigned chunkIndex = 0);
+static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type,
+ const LateParsedAttrList &LateAttrs,
+ unsigned chunkIndex = 0);
static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
QualType &type, CUDAFunctionTarget CFT);
@@ -8870,18 +8872,18 @@ static unsigned getPointerNestLevel(TypeProcessingState &state,
if (chunkIndex > 0) {
const auto &stateDeclarator = state.getDeclarator();
assert(chunkIndex <= stateDeclarator.getNumTypeObjects());
- // DeclChunks are ordered identifier out. Index 0 is the outer most type object.
- // Find outer pointer, array or function.
+ // DeclChunks are ordered identifier out. Index 0 is the outer most type
+ // object. Find outer pointer, array or function.
for (unsigned i = 0; i < chunkIndex; ++i) {
auto TypeObject = stateDeclarator.getTypeObject(i);
switch (TypeObject.Kind) {
- case DeclaratorChunk::Function:
- case DeclaratorChunk::Array:
- case DeclaratorChunk::Pointer:
- pointerNestLevel++;
- break;
- default:
- break;
+ case DeclaratorChunk::Function:
+ case DeclaratorChunk::Array:
+ case DeclaratorChunk::Pointer:
+ pointerNestLevel++;
+ break;
+ default:
+ break;
}
}
}
@@ -8889,10 +8891,10 @@ static unsigned getPointerNestLevel(TypeProcessingState &state,
}
static bool validateCountedByAttrType(Sema &S, QualType Ty,
- ParsedAttr::Kind AttrKind,
- SourceLocation AttrLoc,
- unsigned pointerNestLevel,
- bool &CountInBytes, bool &OrNull) {
+ ParsedAttr::Kind AttrKind,
+ SourceLocation AttrLoc,
+ unsigned pointerNestLevel,
+ bool &CountInBytes, bool &OrNull) {
switch (AttrKind) {
case ParsedAttr::AT_CountedBy:
CountInBytes = false;
@@ -8974,8 +8976,7 @@ static bool validateCountedByAttrType(Sema &S, QualType Ty,
}
if (pointerNestLevel > 0) {
- S.Diag(AttrLoc, diag::err_counted_by_on_nested_pointer)
- << Kind;
+ S.Diag(AttrLoc, diag::err_counted_by_on_nested_pointer) << Kind;
return false;
}
@@ -8983,7 +8984,7 @@ static bool validateCountedByAttrType(Sema &S, QualType Ty,
}
static void HandleCountedByAttrOnType(TypeProcessingState &State,
- QualType &CurType, ParsedAttr &Attr) {
+ QualType &CurType, ParsedAttr &Attr) {
Sema &S = State.getSema();
// This attribute is only supported in C.
@@ -9001,8 +9002,8 @@ static void HandleCountedByAttrOnType(TypeProcessingState &State,
// This is a mechanism to prevent nested count pointer types in the contexts
// where late parsing isn't allowed: currently that is any context other than
- // struct fields. In the context where late parsing is allowed, the level check
- // will be done once the whole context is constructed.
+ // struct fields. In the context where late parsing is allowed, the level
+ // check will be done once the whole context is constructed.
unsigned chunkIndex = State.getCurrentChunkIndex();
unsigned pointerNestLevel = 0;
@@ -9014,16 +9015,18 @@ static void HandleCountedByAttrOnType(TypeProcessingState &State,
bool CountInBytes, OrNull;
if (!validateCountedByAttrType(S, CurType, Attr.getKind(), Attr.getLoc(),
- pointerNestLevel, CountInBytes, OrNull)) {
+ pointerNestLevel, CountInBytes, OrNull)) {
Attr.setInvalid();
return;
}
CurType = S.BuildCountAttributedArrayOrPointerType(CurType, CountExpr,
- CountInBytes, OrNull);
+ CountInBytes, OrNull);
}
-static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type, const LateParsedAttrList &LateAttrs, unsigned chunkIndex) {
+static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type,
+ const LateParsedAttrList &LateAttrs,
+ unsigned chunkIndex) {
if (LateAttrs.empty())
return true;
@@ -9034,12 +9037,12 @@ static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type, con
unsigned pointerNestLevel = 0;
for (auto *LA : LateAttrs) {
- ParsedAttr::Kind AttrKind =
- ParsedAttr::getParsedKind(&LA->AttrName, nullptr, ParsedAttr::Form::GNU().getSyntax());
+ ParsedAttr::Kind AttrKind = ParsedAttr::getParsedKind(
+ &LA->AttrName, nullptr, ParsedAttr::Form::GNU().getSyntax());
bool CountInBytes, OrNull;
if (!validateCountedByAttrType(S, type, AttrKind, LA->AttrNameLoc,
- pointerNestLevel, CountInBytes, OrNull))
+ pointerNestLevel, CountInBytes, OrNull))
return false;
type = S.getASTContext().getLateParsedAttrType(
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 2df62b34834fa..6cc06afac99ec 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7731,8 +7731,9 @@ QualType TreeTransform<Derived>::TransformCountAttributedType(
}
template <typename Derived>
-QualType TreeTransform<Derived>::TransformLateParsedAttrType(
- TypeLocBuilder &TLB, LateParsedAttrTypeLoc TL) {
+QualType
+TreeTransform<Derived>::TransformLateParsedAttrType(TypeLocBuilder &TLB,
+ LateParsedAttrTypeLoc TL) {
const LateParsedAttrType *OldTy = TL.getTypePtr();
QualType InnerTy = getDerived().TransformType(TLB, TL.getInnerLoc());
if (InnerTy.isNull())
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 388b1f3924ec4..bf2c608777b0e 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -157,7 +157,8 @@ static TypeCode getTypeCodeForTypeClass(Type::TypeClass id) {
case Type::CLASS_ID: return TYPE_##CODE_ID;
#include "clang/Serialization/TypeBitCodes.def"
case Type::LateParsedAttr:
- llvm_unreachable("should be replaced with a concrete type before serialization");
+ llvm_unreachable(
+ "should be replaced with a concrete type before serialization");
case Type::Builtin:
llvm_unreachable("shouldn't be serializing a builtin type this way");
}
>From 2962cc9a98abee39acbf8989ddf7f154b5c7d4f4 Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Sat, 7 Feb 2026 13:45:28 -0800
Subject: [PATCH 05/10] Fix location to trigger late parsing for fields
---
clang/include/clang/Sema/Sema.h | 3 +
clang/lib/Parse/ParseDecl.cpp | 10 ++++
clang/lib/Sema/SemaDecl.cpp | 57 ++++++++++++-------
.../attr-counted-by-late-parsed-struct-ptrs.c | 4 --
4 files changed, 48 insertions(+), 26 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b8f3e12382052..78e7def27aa84 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4346,6 +4346,9 @@ class Sema final : public SemaBase {
ArrayRef<Decl *> Fields, SourceLocation LBrac,
SourceLocation RBrac, const ParsedAttributesView &AttrList);
+ /// Transform field types that contain late-parsed type attributes.
+ void ProcessLateParsedTypeAttributes(RecordDecl *EnclosingDecl);
+
/// ActOnTagStartDefinition - Invoked when we have entered the
/// scope of a tag's definition (e.g., for an enumeration, class,
/// struct, or union).
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 14b7eda4b8dc3..b2fcd3579a2cc 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5022,6 +5022,12 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
// Parse all the comma separated declarators.
ParsingDeclSpec DS(*this);
ParseStructDeclaration(DS, CFieldCallback, &LateFieldAttrs);
+ if (DS.getTypeSpecType() == TST_struct) {
+ auto *RD = dyn_cast<RecordDecl>(DS.getRepAsDecl());
+ if (RD && !RD->isAnonymousStructOrUnion()) {
+ Actions.ProcessLateParsedTypeAttributes(RD);
+ }
+ }
} else { // Handle @defs
ConsumeToken();
if (!Tok.isObjCAtKeyword(tok::objc_defs)) {
@@ -5074,6 +5080,10 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
// pointer and cached tokens) to parse themselves.
Actions.ActOnFields(getCurScope(), RecordLoc, TagDecl, FieldDecls,
T.getOpenLocation(), T.getCloseLocation(), attrs);
+ Scope *ParentScope = getCurScope()->getParent();
+ assert(ParentScope);
+ if (!ParentScope->getEntity()->isRecord())
+ Actions.ProcessLateParsedTypeAttributes(TagDecl);
StructScope.Exit();
Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, T.getRange());
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 25019314a394e..8ccb3b1a7d92e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -19956,6 +19956,34 @@ struct RebuildTypeWithLateParsedAttr
}
};
+void Sema::ProcessLateParsedTypeAttributes(RecordDecl *EnclosingDecl) {
+ for (auto *I : EnclosingDecl->decls()) {
+ // We only process FieldDecl here, not IndirectFieldDecl, because
+ // ActOnFields is called on the struct that directly contains the field.
+ // TODO: To support counted_by referring to a field in an anonymous struct
+ // declared later, late parsing should be triggered from the innermost
+ // named parent struct.
+ FieldDecl *FD = dyn_cast<FieldDecl>(I);
+ IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(I);
+ if (!FD && IFD) {
+ FD = IFD->getAnonField();
+ }
+ if (!FD)
+ continue;
+
+ RebuildTypeWithLateParsedAttr RebuildFieldType(*this, FD);
+ auto *OldTSI = FD->getTypeSourceInfo();
+ auto *TSI = RebuildFieldType.TransformType(FD->getTypeSourceInfo());
+ if (TSI && TSI != OldTSI) {
+ FD->setTypeSourceInfo(TSI);
+ FD->setType(TSI->getType());
+ if (IFD) {
+ IFD->setType(TSI->getType());
+ }
+ }
+ }
+}
+
void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
ArrayRef<Decl *> Fields, SourceLocation LBrac,
SourceLocation RBrac,
@@ -19997,28 +20025,13 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
// Late-parsed type attributes are stored as placeholder LateParsedAttrType
// nodes. We need to transform them into proper attributed types now that
// all fields are visible and can be referenced.
- // This is only enabled when -fexperimental-late-parse-attributes is set.
- if (getLangOpts().ExperimentalLateParseAttributes) {
- for (ArrayRef<Decl *>::iterator i = Fields.begin(), end = Fields.end();
- i != end; ++i) {
- // We only process FieldDecl here, not IndirectFieldDecl, because
- // ActOnFields is called on the struct that directly contains the field.
- // TODO: To support counted_by referring to a field in an anonymous struct
- // declared later, late parsing should be triggered from the innermost
- // named parent struct.
- FieldDecl *FD = dyn_cast<FieldDecl>(*i);
- if (FD->getType()->isRecordType())
- continue;
-
- RebuildTypeWithLateParsedAttr RebuildFieldType(*this, FD);
- auto *OldTSI = FD->getTypeSourceInfo();
- auto *TSI = RebuildFieldType.TransformType(FD->getTypeSourceInfo());
- if (TSI && TSI != OldTSI) {
- FD->setTypeSourceInfo(TSI);
- FD->setType(TSI->getType());
- }
- }
- }
+ // This is only enabled when -fexperimental-late-parse-attributes is set
+ // If late parsing here only if it's top-level record type. Otherwise, wait
+ // until struct declaration is.
+ // if (getLangOpts().ExperimentalLateParseAttributes &&
+ // !EnclosingDecl->getDeclContext()->isRecord()) {
+ // ProcessLateParsedTypeAttributes(Fields);
+ // }
// Perform FieldDecl-dependent validation for counted_by family attributes
for (ArrayRef<Decl *>::iterator i = Fields.begin(), end = Fields.end();
diff --git a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
index dad463dd274a6..19276c322eb6f 100644
--- a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
@@ -87,9 +87,7 @@ struct on_member_pointer_struct_with_annotated_vla {
};
struct on_pointer_anon_buf {
- // TODO: Support referring to parent scope
struct {
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known *buf __counted_by(count);
};
int count;
@@ -205,8 +203,6 @@ struct on_nested_pointer_flexible_array_inner {
struct on_pointer_anon_buf_ty_pos {
struct {
- // TODO: Support referring to parent scope
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known * __counted_by(count) buf;
};
int count;
>From 16d7c008486407386d861c86eabb44faee34ee4f Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Sat, 7 Feb 2026 13:48:31 -0800
Subject: [PATCH 06/10] Remove TypePrinter changes added for debugging
---
clang/lib/AST/TypePrinter.cpp | 16 ++--------------
1 file changed, 2 insertions(+), 14 deletions(-)
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index f7fe991e6f9f6..60fa89dc5c735 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -32,7 +32,6 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
-#include "clang/Sema/DeclSpec.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallString.h"
@@ -1853,29 +1852,18 @@ void TypePrinter::printCountAttributedAfter(const CountAttributedType *T,
printCountAttributedImpl(T, OS, Policy);
}
-static void printLateParsedAttrImpl(const LateParsedAttrType *T,
- raw_ostream &OS) {
- OS << " LateParsedTypeAttr::"
- << T->getLateParsedAttribute()->AttrName.getName() << "()";
-}
-
void TypePrinter::printLateParsedAttrBefore(const LateParsedAttrType *T,
raw_ostream &OS) {
// LateParsedAttrType is a transient placeholder that should not appear
- // in final output. Print it explicitly for debugging purposes.
+ // in user-facing output. Just print the wrapped type.
printBefore(T->getWrappedType(), OS);
- if (!T->isArrayType())
- printLateParsedAttrImpl(T, OS);
}
void TypePrinter::printLateParsedAttrAfter(const LateParsedAttrType *T,
raw_ostream &OS) {
// LateParsedAttrType is a transient placeholder that should not appear
- // in final output. The attribute name is already printed in printBefore
- // for pointer types, but for array types we print it here.
+ // in user-facing output. Just print the wrapped type.
printAfter(T->getWrappedType(), OS);
- if (T->isArrayType())
- printLateParsedAttrImpl(T, OS);
}
void TypePrinter::printAttributedBefore(const AttributedType *T,
>From 718885a7423e75b185f6b706d960c9bd0e82bf3f Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Sat, 7 Feb 2026 16:12:56 -0800
Subject: [PATCH 07/10] Fix-up late parsing for fields
---
clang/lib/Parse/ParseDecl.cpp | 18 +++++++++---------
clang/lib/Sema/SemaDecl.cpp | 12 ++++++------
.../attr-counted-by-late-parsed-struct-ptrs.c | 3 ++-
...ounted-by-or-null-late-parsed-struct-ptrs.c | 4 ----
.../attr-sized-by-late-parsed-struct-ptrs.c | 4 ----
...-sized-by-or-null-late-parsed-struct-ptrs.c | 4 ----
6 files changed, 17 insertions(+), 28 deletions(-)
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index b2fcd3579a2cc..7d617238cf1e9 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4783,11 +4783,6 @@ void Parser::ParseStructDeclaration(
} else
DeclaratorInfo.D.SetIdentifier(nullptr, Tok.getLocation());
- // Here, we now know that the unnamed struct is not an anonymous struct.
- // Report an error if a counted_by attribute refers to a field in a
- // different named struct.
- DiagnoseCountAttributedTypeInUnnamedAnon(DS, *this);
-
if (TryConsumeToken(tok::colon)) {
ExprResult Res(ParseConstantExpression());
if (Res.isInvalid())
@@ -5023,9 +5018,14 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
ParsingDeclSpec DS(*this);
ParseStructDeclaration(DS, CFieldCallback, &LateFieldAttrs);
if (DS.getTypeSpecType() == TST_struct) {
- auto *RD = dyn_cast<RecordDecl>(DS.getRepAsDecl());
- if (RD && !RD->isAnonymousStructOrUnion()) {
- Actions.ProcessLateParsedTypeAttributes(RD);
+
+ if (getLangOpts().ExperimentalLateParseAttributes) {
+ auto *RD = dyn_cast<RecordDecl>(DS.getRepAsDecl());
+ if (RD && !RD->isAnonymousStructOrUnion()) {
+ Actions.ProcessLateParsedTypeAttributes(RD);
+ }
+ } else {
+ DiagnoseCountAttributedTypeInUnnamedAnon(DS, *this);
}
}
} else { // Handle @defs
@@ -5082,7 +5082,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
T.getOpenLocation(), T.getCloseLocation(), attrs);
Scope *ParentScope = getCurScope()->getParent();
assert(ParentScope);
- if (!ParentScope->getEntity()->isRecord())
+ if (getLangOpts().ExperimentalLateParseAttributes && !ParentScope->getEntity()->isRecord())
Actions.ProcessLateParsedTypeAttributes(TagDecl);
StructScope.Exit();
Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, T.getRange());
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8ccb3b1a7d92e..b975629510cf8 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -19958,17 +19958,12 @@ struct RebuildTypeWithLateParsedAttr
void Sema::ProcessLateParsedTypeAttributes(RecordDecl *EnclosingDecl) {
for (auto *I : EnclosingDecl->decls()) {
- // We only process FieldDecl here, not IndirectFieldDecl, because
- // ActOnFields is called on the struct that directly contains the field.
- // TODO: To support counted_by referring to a field in an anonymous struct
- // declared later, late parsing should be triggered from the innermost
- // named parent struct.
FieldDecl *FD = dyn_cast<FieldDecl>(I);
IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(I);
if (!FD && IFD) {
FD = IFD->getAnonField();
}
- if (!FD)
+ if (!FD || FD->getType()->isRecordType())
continue;
RebuildTypeWithLateParsedAttr RebuildFieldType(*this, FD);
@@ -19981,6 +19976,11 @@ void Sema::ProcessLateParsedTypeAttributes(RecordDecl *EnclosingDecl) {
IFD->setType(TSI->getType());
}
}
+
+ if (auto *CAT = FD->getType()->getAs<CountAttributedType>()) {
+ CheckCountedByAttrOnFieldDecl(FD, CAT->getCountExpr(),
+ CAT->isCountInBytes(), CAT->isOrNull());
+ }
}
}
diff --git a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
index 19276c322eb6f..554bcfbd8d5c7 100644
--- a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
@@ -196,9 +196,10 @@ struct on_nested_pointer_array_inner {
};
struct on_nested_pointer_flexible_array_inner {
+ // expected-error at +2{{flexible array member 'arr' with type 'struct size_known *[]' is not at the end of struct}}
// expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
struct size_known *__counted_by(count) arr[];
- int count;
+ int count; // expected-note{{next field declaration is here}}
};
struct on_pointer_anon_buf_ty_pos {
diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
index a6e4c177042bc..9f91f66b6e1c4 100644
--- a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
@@ -87,9 +87,7 @@ struct on_member_pointer_struct_with_annotated_vla {
};
struct on_pointer_anon_buf {
- // TODO: Support referring to parent scope
struct {
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known *buf __counted_by_or_null(count);
};
int count;
@@ -192,8 +190,6 @@ struct on_nested_pointer_outer {
struct on_pointer_anon_buf_ty_pos {
struct {
- // TODO: Support referring to parent scope
- // expected-error at +1{{use of undeclared identifier 'count'}}
struct size_known * __counted_by_or_null(count) buf;
};
int count;
diff --git a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
index 8b4fef63a89c7..a985030c38b43 100644
--- a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
@@ -84,9 +84,7 @@ struct on_member_pointer_struct_with_annotated_vla {
};
struct on_pointer_anon_buf {
- // TODO: Support referring to parent scope
struct {
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known *buf __sized_by(size);
};
int size;
@@ -189,8 +187,6 @@ struct on_nested_pointer_outer {
struct on_pointer_anon_buf_ty_pos {
struct {
- // TODO: Support referring to parent scope
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known * __sized_by(size) buf;
};
int size;
diff --git a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
index 34aaec9ec83d1..392db90adab92 100644
--- a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
@@ -84,9 +84,7 @@ struct on_member_pointer_struct_with_annotated_vla {
};
struct on_pointer_anon_buf {
- // TODO: Support referring to parent scope
struct {
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known *buf __sized_by_or_null(size);
};
int size;
@@ -192,8 +190,6 @@ struct on_nested_pointer_outer {
struct on_pointer_anon_buf_ty_pos {
struct {
- // TODO: Support referring to parent scope
- // expected-error at +1{{use of undeclared identifier 'size'}}
struct size_known * __sized_by_or_null(size) buf;
};
int size;
>From 23b6b064697f24c03ad88ba1ddba519f7122fb79 Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Tue, 10 Feb 2026 14:00:19 -0800
Subject: [PATCH 08/10] Cleanup: remove unused functions & clang-format
---
clang/include/clang/Parse/Parser.h | 3 ---
clang/include/clang/Sema/DeclSpec.h | 2 --
clang/lib/Parse/ParseDecl.cpp | 13 +++----------
3 files changed, 3 insertions(+), 15 deletions(-)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index d5c18ab9a045f..f064ca08acaf8 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1429,9 +1429,6 @@ class Parser : public CodeCompletionHandler {
void ParseLexedTypeAttribute(LateParsedTypeAttribute &LA, bool EnterScope,
ParsedAttributes &OutAttrs);
- static void LateTypeAttrParserCallback(void *P, void *LA, bool EnterScope,
- ParsedAttributes &OutAttrs);
-
void ParseLexedPragmas(ParsingClass &Class);
void ParseLexedPragma(LateParsedPragma &LP);
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 18b4f729d07b6..14070a05a108f 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -131,8 +131,6 @@ namespace clang {
void ParseLexedAttributes() override;
void ParseLexedTypeAttributes() override;
- void addDecl(Decl *D) { Decls.push_back(D); }
-
/// Parse this late-parsed type attribute and store results in OutAttrs.
/// This method can be called from Sema during type transformation to
/// parse the cached tokens and produce the final attribute.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7d617238cf1e9..b2004ca986278 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4926,14 +4926,6 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
}
}
-void Parser::LateTypeAttrParserCallback(void *P, void *OLA, bool EnterScope,
- ParsedAttributes &OutAttrs) {
- auto *LA = static_cast<LateParsedAttribute *>(OLA);
- auto *LTA = cast<LateParsedTypeAttribute>(LA);
- return ((Parser *)(P))
- ->ParseLexedTypeAttribute(*LTA, /*EnterScope=*/false, OutAttrs);
-}
-
void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
DeclSpec::TST TagType, RecordDecl *TagDecl) {
PrettyDeclStackTraceEntry CrashInfo(Actions.Context, TagDecl, RecordLoc,
@@ -5022,7 +5014,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
if (getLangOpts().ExperimentalLateParseAttributes) {
auto *RD = dyn_cast<RecordDecl>(DS.getRepAsDecl());
if (RD && !RD->isAnonymousStructOrUnion()) {
- Actions.ProcessLateParsedTypeAttributes(RD);
+ Actions.ProcessLateParsedTypeAttributes(RD);
}
} else {
DiagnoseCountAttributedTypeInUnnamedAnon(DS, *this);
@@ -5082,7 +5074,8 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
T.getOpenLocation(), T.getCloseLocation(), attrs);
Scope *ParentScope = getCurScope()->getParent();
assert(ParentScope);
- if (getLangOpts().ExperimentalLateParseAttributes && !ParentScope->getEntity()->isRecord())
+ if (getLangOpts().ExperimentalLateParseAttributes &&
+ !ParentScope->getEntity()->isRecord())
Actions.ProcessLateParsedTypeAttributes(TagDecl);
StructScope.Exit();
Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, T.getRange());
>From 88e6820a67c2c397da524ede4d2c7fd5fd518a0d Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Thu, 12 Feb 2026 18:44:12 -0800
Subject: [PATCH 09/10] Store AttrNameLoc in LateParsedAttrTypeLoc to maintain
AST/Sema layering
Store the attribute source location directly in LateParsedAttrLocInfo
instead of accessing it through LateParsedTypeAttribute pointer. This
avoids an unwanted dependency from AST layer to Sema/Parser layer.
---
clang/include/clang/AST/ASTContext.h | 2 +-
clang/include/clang/AST/TypeLoc.h | 16 +++++++++++++---
clang/include/clang/Sema/DeclSpec.h | 1 -
clang/lib/AST/ASTContext.cpp | 1 -
clang/lib/AST/TypeLoc.cpp | 9 ---------
clang/lib/Sema/SemaDecl.cpp | 5 +++++
clang/lib/Sema/SemaType.cpp | 20 +++++++++++++++++---
clang/lib/Sema/TreeTransform.h | 3 ++-
8 files changed, 38 insertions(+), 19 deletions(-)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index cbd70dbc4edab..919a16f7119f7 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -101,13 +101,13 @@ class CXXConstructorDecl;
class CXXMethodDecl;
class CXXRecordDecl;
class DiagnosticsEngine;
-struct LateParsedTypeAttribute;
class DynTypedNodeList;
class Expr;
enum class FloatModeKind;
class GlobalDecl;
class IdentifierTable;
class LangOptions;
+struct LateParsedTypeAttribute;
class MangleContext;
class MangleNumberingContext;
class MemberSpecializationInfo;
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 85d6937f3bbb9..0124b84201a74 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -1329,7 +1329,9 @@ class CountAttributedTypeLoc final
SourceRange getLocalSourceRange() const;
};
-struct LateParsedAttrLocInfo {}; // Nothing.
+struct LateParsedAttrLocInfo {
+ SourceLocation AttrNameLoc;
+};
class LateParsedAttrTypeLoc
: public ConcreteTypeLoc<UnqualTypeLoc, LateParsedAttrTypeLoc,
@@ -1337,11 +1339,19 @@ class LateParsedAttrTypeLoc
public:
TypeLoc getInnerLoc() const { return getInnerTypeLoc(); }
+ SourceLocation getAttrNameLoc() const { return getLocalData()->AttrNameLoc; }
+
+ void setAttrNameLoc(SourceLocation Loc) { getLocalData()->AttrNameLoc = Loc; }
+
+ SourceRange getLocalSourceRange() const {
+ return SourceRange(getAttrNameLoc(), getAttrNameLoc());
+ }
+
void initializeLocal(ASTContext &Context, SourceLocation Loc) {
- // Nothing to initialize
+ setAttrNameLoc(Loc);
}
- SourceRange getLocalSourceRange() const;
+ unsigned getLocalDataSize() const { return sizeof(LateParsedAttrLocInfo); }
QualType getInnerType() const { return getTypePtr()->getWrappedType(); }
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 14070a05a108f..aaac0e8b7259f 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -976,7 +976,6 @@ class DeclSpec {
ParsedAttributes &getAttributes() { return Attrs; }
const ParsedAttributes &getAttributes() const { return Attrs; }
- LateParsedAttrList *getLateAttributePtr() { return &LateParsedAttrs; }
LateParsedAttrList &getLateAttributes() { return LateParsedAttrs; }
const LateParsedAttrList &getLateAttributes() const {
return LateParsedAttrs;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index d2fff816a0371..d69c2eeb978a9 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -65,7 +65,6 @@
#include "clang/Basic/TargetCXXABI.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/XRayLists.h"
-#include "clang/Sema/DeclSpec.h"
#include "llvm/ADT/APFixedPoint.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APSInt.h"
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index b4b0d45ee1899..f54ccf0932bc7 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -22,7 +22,6 @@
#include "clang/AST/TypeLocVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
-#include "clang/Sema/DeclSpec.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
@@ -595,14 +594,6 @@ SourceRange CountAttributedTypeLoc::getLocalSourceRange() const {
return getCountExpr() ? getCountExpr()->getSourceRange() : SourceRange();
}
-SourceRange LateParsedAttrTypeLoc::getLocalSourceRange() const {
- // LateParsedAttrType is a transient type that wraps an unparsed attribute.
- // Return the attribute's name location if available.
- if (auto *Attr = getLateParsedAttribute())
- return SourceRange(Attr->AttrNameLoc);
- return {};
-}
-
SourceRange BTFTagAttributedTypeLoc::getLocalSourceRange() const {
return getAttr() ? getAttr()->getRange() : SourceRange();
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b975629510cf8..aca7bf2870730 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -19734,6 +19734,11 @@ struct RebuildTypeWithLateParsedAttr
// Parse the cached attribute tokens
LTA->ParseInto(Attrs);
+ // LateParsedTypeAttribute is no longer needed so delete it. Ideally,
+ // LateParsedAttrType would own this object, but LateParsedTypeAttribute
+ // is intentionally forward declared to avoid making the AST depend on
+ // Sema/Parser components.
+ delete LTA;
// Invalid argument
if (Attrs.empty())
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 5d5ea63fe25e2..e40922ea27b1a 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -5923,6 +5923,11 @@ namespace {
Visit(TL.getModifiedLoc());
fillAttributedTypeLoc(TL, State);
}
+ void VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
+ Visit(TL.getInnerLoc());
+ if (auto *LateAttr = TL.getLateParsedAttribute())
+ TL.setAttrNameLoc(LateAttr->AttrNameLoc);
+ }
void VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
Visit(TL.getWrappedLoc());
}
@@ -6209,7 +6214,8 @@ namespace {
// nothing
}
void VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
- // nothing
+ if (auto *LateAttr = TL.getLateParsedAttribute())
+ TL.setAttrNameLoc(LateAttr->AttrNameLoc);
}
void VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// nothing
@@ -6375,8 +6381,16 @@ GetTypeSourceInfoForDeclarator(TypeProcessingState &State,
break;
}
+ case TypeLoc::LateParsedAttr: {
+ auto TL = CurrTL.castAs<LateParsedAttrTypeLoc>();
+ if (auto *LateAttr = TL.getLateParsedAttribute()) {
+ TL.setAttrNameLoc(LateAttr->AttrNameLoc);
+ }
+ CurrTL = TL.getNextTypeLoc().getUnqualifiedLoc();
+ break;
+ }
+
case TypeLoc::CountAttributed:
- case TypeLoc::LateParsedAttr:
case TypeLoc::Adjusted:
case TypeLoc::BTFTagAttributed: {
CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc();
@@ -9046,7 +9060,7 @@ static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type,
return false;
type = S.getASTContext().getLateParsedAttrType(
- type, static_cast<LateParsedTypeAttribute *>(LA));
+ type, dyn_cast<LateParsedTypeAttribute>(LA));
}
return true;
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 6cc06afac99ec..d727ae4c09b1b 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7745,7 +7745,8 @@ TreeTransform<Derived>::TransformLateParsedAttrType(TypeLocBuilder &TLB,
InnerTy, OldTy->getLateParsedAttribute());
}
- TLB.push<LateParsedAttrTypeLoc>(Result);
+ LateParsedAttrTypeLoc newTL = TLB.push<LateParsedAttrTypeLoc>(Result);
+ newTL.setAttrNameLoc(TL.getAttrNameLoc());
return Result;
}
>From 8f1dcc83e7aebf4c2318c9486951a2fa784fb13a Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Fri, 13 Feb 2026 07:33:06 -0800
Subject: [PATCH 10/10] fix format
---
clang/include/clang/Sema/DeclSpec.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index aaac0e8b7259f..ad7f17416c11b 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1358,7 +1358,7 @@ class UnqualifiedId {
///
/// This is intended to be a small value object.
struct DeclaratorChunk {
- DeclaratorChunk() : LateAttrList(true, true, true){};
+ DeclaratorChunk() : LateAttrList(true, true, true) {};
enum {
Pointer, Reference, Array, Function, BlockPointer, MemberPointer, Paren, Pipe
More information about the cfe-commits
mailing list