[clang] [BoundsSafety] Support late parsing for `counted_by` in type positions (PR #166491)
Yeoul Na via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 10 12:57:39 PST 2025
https://github.com/rapidsna updated https://github.com/llvm/llvm-project/pull/166491
>From 1f6e6d16356fb3381c0ed39152b862fd00469064 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 1/4] [BoundsSafety] Support late parsing for `counted_by` in
type positions
Previously, late parsing only supported attributes in declaration
position and could not resolve references to member declarations that
appeared later in the struct. Additionally, the compiler incorrectly
accepted `counted_by` on nested pointer types, which should be rejected.
Instead, these attributes were incorrectly attached to the outermost
pointer type because they were being treated as GNU-style declaration
attributes rather than type attributes.
This commit adds a vector of `LateParsedAttribute *` to
`DeclaratorChunk`, similar to how `ParsedAttr` is attached to
`DeclaratorChunk`. Since DeclSpec doesn't see the definition of
`LateParsedAttribute`, it is treated as an opaque pointer type. When
the late-attribute is actually parsed, it is now correctly attached
to the appropriate nested type level, enabling references to members
declared later when the late-parsed attribute is in type position, and
rejection of invalid nested pointer cases.
Issue #166411
---
.../clang/Basic/DiagnosticSemaKinds.td | 3 +
clang/include/clang/Parse/Parser.h | 31 ++--
clang/include/clang/Sema/DeclSpec.h | 8 +-
clang/include/clang/Sema/Sema.h | 10 +-
clang/lib/Parse/ParseDecl.cpp | 81 +++++---
clang/lib/Sema/SemaBoundsSafety.cpp | 10 +-
clang/lib/Sema/SemaDecl.cpp | 6 +-
clang/lib/Sema/SemaDeclAttr.cpp | 32 +++-
.../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 | 49 +----
...unted-by-or-null-late-parsed-struct-ptrs.c | 49 +----
...ruct-ptrs-completable-incomplete-pointee.c | 33 ++--
.../attr-counted-by-or-null-struct-ptrs.c | 8 +-
...ruct-ptrs-completable-incomplete-pointee.c | 32 ++--
clang/test/Sema/attr-counted-by-struct-ptrs.c | 8 +-
.../attr-sized-by-late-parsed-struct-ptrs.c | 39 +---
...sized-by-or-null-late-parsed-struct-ptrs.c | 46 +----
.../Sema/attr-sized-by-or-null-struct-ptrs.c | 8 +-
clang/test/Sema/attr-sized-by-struct-ptrs.c | 6 +-
23 files changed, 368 insertions(+), 361 deletions(-)
create mode 100644 clang/test/Sema/attr-bounds-safety-function-ptr-param.c
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3e864475f22a1..2d6554f66bced 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7012,6 +7012,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/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index dad8efd0f017f..8f7c921fb2b1d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1161,10 +1161,11 @@ class Parser : public CodeCompletionHandler {
IdentifierInfo *MacroII = nullptr;
SourceLocation AttrNameLoc;
SmallVector<Decl *, 2> Decls;
+ unsigned NestedTypeLevel;
explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
- SourceLocation Loc)
- : Self(P), AttrName(Name), AttrNameLoc(Loc) {}
+ SourceLocation Loc, unsigned Level = 0)
+ : Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(Level) {}
void ParseLexedAttributes() override;
@@ -1889,10 +1890,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 +1906,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
@@ -2444,7 +2449,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 +2616,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..9f633dd71c3f6 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1238,6 +1238,9 @@ struct DeclaratorChunk {
ParsedAttributesView AttrList;
+ using LateAttrListTy = SmallVector<void *, 1>;
+ LateAttrListTy LateAttrList;
+
struct PointerTypeInfo {
/// The type qualifiers: const/volatile/restrict/unaligned/atomic.
LLVM_PREFERRED_TYPE(DeclSpec::TQ)
@@ -2325,13 +2328,16 @@ class Declarator {
/// This function takes attrs by R-Value reference because it takes ownership
/// of those attributes from the parameter.
void AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
- SourceLocation EndLoc) {
+ SourceLocation EndLoc,
+ const DeclaratorChunk::LateAttrListTy &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.assign(LateAttrs);
}
/// AddTypeInfo - Add a chunk to this declarator. Also extend the range to
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 0470645a9e7ad..f940af5448847 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2457,8 +2457,8 @@ class Sema final : public SemaBase {
/// `counted_by_or_null` attribute.
///
/// \returns false iff semantically valid.
- bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
- bool OrNull);
+ bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, unsigned Level,
+ bool CountInBytes, bool OrNull);
/// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
/// `__counted_by_or_null` pointer type \param LHSTy.
@@ -4198,7 +4198,8 @@ class Sema final : public SemaBase {
/// ActOnFinishDelayedAttribute - Invoked when we have finished parsing an
/// attribute for which parsing is delayed.
- void ActOnFinishDelayedAttribute(Scope *S, Decl *D, ParsedAttributes &Attrs);
+ void ActOnFinishDelayedAttribute(Scope *S, Decl *D, ParsedAttributes &Attrs,
+ unsigned NestedTypeLevel = 0);
/// Diagnose any unused parameters in the given sequence of
/// ParmVarDecl pointers.
@@ -5071,7 +5072,8 @@ class Sema final : public SemaBase {
void ProcessDeclAttributeList(Scope *S, Decl *D,
const ParsedAttributesView &AttrList,
const ProcessDeclAttributeOptions &Options =
- ProcessDeclAttributeOptions());
+ ProcessDeclAttributeOptions(),
+ unsigned NestedTypeLevel = 0);
/// Annotation attributes are the only attributes allowed after an access
/// specifier.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 5fcb659768655..a818a51a4e2a1 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -1943,7 +1943,11 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(
ParsedTemplateInfo TemplateInfo;
DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context);
- ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext);
+ // FIXME: Why is PSoon true?
+ LateParsedAttrList BoundsSafetyLateAttrs(
+ /*PSoon=*/true, /*LateAttrParseExperimentalExtOnly=*/true);
+ ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext,
+ &BoundsSafetyLateAttrs);
// If we had a free-standing type definition with a missing semicolon, we
// may get this far before the problem becomes obvious.
@@ -2725,12 +2729,12 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
void Parser::ParseSpecifierQualifierList(
DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
- AccessSpecifier AS, DeclSpecContext DSC) {
+ AccessSpecifier AS, DeclSpecContext DSC, LateParsedAttrList *LateAttrs) {
ParsedTemplateInfo TemplateInfo;
/// 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 +3140,37 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
}
}
-void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
+void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
LateParsedAttrList *LateAttrs) {
if (!LateAttrs)
return;
+ unsigned NestedLevel = 0;
+ for (unsigned i = 0; i < D.getNumTypeObjects(); ++i) {
+ DeclaratorChunk &DC = D.getTypeObject(i);
+
+ switch (DC.Kind) {
+ case DeclaratorChunk::Pointer:
+ case DeclaratorChunk::Array:
+ break;
+ default:
+ continue;
+ }
+
+ // Extract `LateParsedAttribute *` from `DeclaratorChunk`.
+ for (auto *OpaqueLA : DC.LateAttrList) {
+ auto *LA = static_cast<LateParsedAttribute *>(OpaqueLA);
+ LA->NestedTypeLevel = NestedLevel;
+ LateAttrs->push_back(LA);
+ }
+ NestedLevel++;
+ }
+
+ // 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 +3243,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);
}
@@ -4706,7 +4726,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 +4789,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.
@@ -4825,7 +4846,8 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
SourceLocation(), ParsedAttr::Form::GNU(), nullptr);
for (auto *D : LA.Decls)
- Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs);
+ Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs,
+ LA.NestedTypeLevel);
// 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.
@@ -6129,7 +6151,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 +6294,9 @@ void Parser::ParseTypeQualifierListOpt(
// recovery is graceful.
if (AttrReqs & AR_GNUAttributesParsed ||
AttrReqs & AR_GNUAttributesParsedAndRejected) {
- ParseGNUAttributes(DS.getAttributes());
+
+ assert(!LateAttrs || LateAttrs->lateAttrParseExperimentalExtOnly());
+ ParseGNUAttributes(DS.getAttributes(), LateAttrs);
continue; // do *not* consume the next token!
}
// otherwise, FALL THROUGH!
@@ -6452,21 +6477,33 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
((D.getContext() != DeclaratorContext::CXXNew)
? AR_GNUAttributesParsed
: AR_GNUAttributesParsedAndRejected);
+ LateParsedAttrList LateAttrs(/*PSoon=*/true,
+ /*LateAttrParseExperimentalExtOnly=*/true);
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) {
+ DeclaratorChunk::LateAttrListTy OpaqueLateAttrList;
+ if (getLangOpts().ExperimentalLateParseAttributes && !LateAttrs.empty()) {
+ if (!D.isFunctionDeclarator()) {
+ for (auto LA : LateAttrs) {
+ OpaqueLateAttrList.push_back(LA);
+ }
+ }
+ LateAttrs.clear();
+ }
// 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(),
+ OpaqueLateAttrList);
+ } else
// Remember that we parsed a Block type, and remember the type-quals.
D.AddTypeInfo(
DeclaratorChunk::getBlockPointer(DS.getTypeQualifiers(), Loc),
diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp
index de9adf8ef5a1b..f51e2eafd7c6e 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, Expr *E, unsigned Level,
+ bool CountInBytes, bool OrNull) {
// Check the context the attribute is used in
unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
@@ -62,6 +62,12 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
return true;
}
+ if (Level != 0) {
+ Diag(FD->getBeginLoc(), diag::err_counted_by_on_nested_pointer)
+ << Kind << FD->getSourceRange();
+ return true;
+ }
+
const auto FieldTy = FD->getType();
if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
Diag(FD->getBeginLoc(),
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 086dd8ba1c670..67846239d180e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16872,11 +16872,13 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation,
/// When we finish delayed parsing of an attribute, we must attach it to the
/// relevant Decl.
void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D,
- ParsedAttributes &Attrs) {
+ ParsedAttributes &Attrs,
+ unsigned NestedTypeLevel) {
// 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(),
+ NestedTypeLevel);
ProcessAPINotes(D);
if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D))
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a9e7b44ac9d73..2369180713ffb 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6546,7 +6546,8 @@ 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) {
+static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL,
+ unsigned Level) {
auto *FD = dyn_cast<FieldDecl>(D);
assert(FD);
@@ -6577,7 +6578,7 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
llvm_unreachable("unexpected counted_by family attribute");
}
- if (S.CheckCountedByAttrOnField(FD, CountExpr, CountInBytes, OrNull))
+ if (S.CheckCountedByAttrOnField(FD, CountExpr, Level, CountInBytes, OrNull))
return;
QualType CAT = S.BuildCountAttributedArrayOrPointerType(
@@ -6951,7 +6952,8 @@ static bool MustDelayAttributeArguments(const ParsedAttr &AL) {
/// silently ignore it if a GNU attribute.
static void
ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
- const Sema::ProcessDeclAttributeOptions &Options) {
+ const Sema::ProcessDeclAttributeOptions &Options,
+ unsigned NestedTypeLevel = 0) {
if (AL.isInvalid() || AL.getKind() == ParsedAttr::IgnoredAttribute)
return;
@@ -7578,7 +7580,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_CountedByOrNull:
case ParsedAttr::AT_SizedBy:
case ParsedAttr::AT_SizedByOrNull:
- handleCountedByAttrField(S, D, AL);
+ handleCountedByAttrField(S, D, AL, NestedTypeLevel);
break;
// Microsoft attributes:
@@ -7855,14 +7857,15 @@ 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,
+ unsigned NestedTypeLevel) {
if (AttrList.empty())
return;
for (const ParsedAttr &AL : AttrList)
- ProcessDeclAttribute(*this, S, D, AL, Options);
+ ProcessDeclAttribute(*this, S, D, AL, Options, NestedTypeLevel);
// FIXME: We should be able to handle these cases in TableGen.
// GCC accepts
@@ -8171,11 +8174,22 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) {
// position to the decl itself. This handles cases like:
// int *__attr__(x)** D;
// when X is a decl attribute.
+ unsigned NestedTypeLevel = 0;
for (unsigned i = 0, e = PD.getNumTypeObjects(); i != e; ++i) {
ProcessDeclAttributeList(S, D, PD.getTypeObject(i).getAttrs(),
ProcessDeclAttributeOptions()
.WithIncludeCXX11Attributes(false)
- .WithIgnoreTypeAttributes(true));
+ .WithIgnoreTypeAttributes(true),
+ NestedTypeLevel);
+
+ switch (PD.getTypeObject(i).Kind) {
+ case DeclaratorChunk::Pointer:
+ case DeclaratorChunk::Array:
+ NestedTypeLevel++;
+ break;
+ default:
+ break;
+ }
}
// Finally, apply any attributes on the decl itself.
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..5242a1b04d892 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
@@ -106,35 +106,24 @@ 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'}}
+ // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}}
void *__counted_by(count) buf;
int count;
};
@@ -142,79 +131,57 @@ struct on_member_pointer_void_ty_ty_pos {
// -
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' 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;
};
@@ -229,8 +196,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-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
index 233b729f87ccd..81a9d05077b45 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
@@ -107,35 +107,24 @@ 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'}}
+ // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}}
void *__counted_by_or_null(count) buf;
int count;
};
@@ -143,79 +132,57 @@ struct on_member_pointer_void_ty_ty_pos {
// -
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{{'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 +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 '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..dc603f652b43d 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,10 +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_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[];
};
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..3e9140d8f5db6 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,10 +156,9 @@ 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{{'counted_by_or_null' attribute on nested pointer type is not allowed}}
void (* __counted_by_or_null(count) * fn_ptr)(void);
};
@@ -181,9 +178,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..ccff4e07405f4 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,10 +155,9 @@ 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;
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
void (* __counted_by(count) * fn_ptr)(void);
};
@@ -180,9 +177,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` can only be nested when used in function parameters.
int count;
+ // expected-error at +1{{'counted_by' attribute on nested pointer type is not allowed}}
struct size_known *__counted_by(count) *buf;
};
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..0c9ca303be1fd 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
@@ -107,30 +107,21 @@ struct on_pointer_anon_count {
// 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,60 +129,46 @@ 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{{}}
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;
};
@@ -199,14 +176,12 @@ 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.
- // expected-error at +1{{use of undeclared identifier 'size'}}
+ // expected-error at +1{{}}
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 +196,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..19e63d74de70b 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,59 @@ 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'}}
+ // 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{{'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 +193,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..9ff681c8a863a 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,10 +149,9 @@ 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{{sized_by_or_null' attribute on nested pointer type is not allowed}}
void (* __sized_by_or_null(size) * fn_ptr)(void);
};
@@ -171,9 +168,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..9783f31913ba0 100644
--- a/clang/test/Sema/attr-sized-by-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-struct-ptrs.c
@@ -151,10 +151,9 @@ 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{{'sized_by' attribute on nested pointer type is not allowed}}
void (* __sized_by(size) * fn_ptr)(void);
};
@@ -171,9 +170,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;
};
>From 660ad322870f0b5d45592f522660fdccb74dd720 Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Mon, 10 Nov 2025 10:45:46 -0800
Subject: [PATCH 2/4] Add clarifying comments; Fix empty diagostic text; Use
ArrayRef for parameter
---
clang/include/clang/Parse/Parser.h | 4 ++--
clang/include/clang/Sema/DeclSpec.h | 8 ++++++--
clang/lib/Parse/ParseDecl.cpp | 10 ++++++----
.../test/Sema/attr-sized-by-late-parsed-struct-ptrs.c | 9 ++-------
.../attr-sized-by-or-null-late-parsed-struct-ptrs.c | 1 +
5 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 8f7c921fb2b1d..f2f969e7b2de1 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1164,8 +1164,8 @@ class Parser : public CodeCompletionHandler {
unsigned NestedTypeLevel;
explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
- SourceLocation Loc, unsigned Level = 0)
- : Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(Level) {}
+ SourceLocation Loc, unsigned NestedTypeLevel = 0)
+ : Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(NestedTypeLevel) {}
void ParseLexedAttributes() override;
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 9f633dd71c3f6..fd5ca9b0d089f 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1238,7 +1238,11 @@ struct DeclaratorChunk {
ParsedAttributesView AttrList;
- using LateAttrListTy = SmallVector<void *, 1>;
+ /// Stores pointers to `Parser::LateParsedAttribute`. We use `void*` here
+ /// because `LateParsedAttribute` is a nested struct of `class Parser` and
+ /// cannot be forward-declared.
+ using LateAttrOpaquePtr = void *;
+ using LateAttrListTy = SmallVector<LateAttrOpaquePtr, 1>;
LateAttrListTy LateAttrList;
struct PointerTypeInfo {
@@ -2329,7 +2333,7 @@ class Declarator {
/// of those attributes from the parameter.
void AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
SourceLocation EndLoc,
- const DeclaratorChunk::LateAttrListTy &LateAttrs = {}) {
+ ArrayRef<DeclaratorChunk::LateAttrOpaquePtr> LateAttrs = {}) {
DeclTypeInfo.push_back(TI);
DeclTypeInfo.back().getAttrs().prepend(attrs.begin(), attrs.end());
getAttributePool().takeAllFrom(attrs.getPool());
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index a818a51a4e2a1..68cd008f7e358 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3145,7 +3145,7 @@ void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
if (!LateAttrs)
return;
- unsigned NestedLevel = 0;
+ unsigned NestedTypeLevel = 0;
for (unsigned i = 0; i < D.getNumTypeObjects(); ++i) {
DeclaratorChunk &DC = D.getTypeObject(i);
@@ -3160,10 +3160,10 @@ void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
// Extract `LateParsedAttribute *` from `DeclaratorChunk`.
for (auto *OpaqueLA : DC.LateAttrList) {
auto *LA = static_cast<LateParsedAttribute *>(OpaqueLA);
- LA->NestedTypeLevel = NestedLevel;
+ LA->NestedTypeLevel = NestedTypeLevel;
LateAttrs->push_back(LA);
}
- NestedLevel++;
+ NestedTypeLevel++;
}
// Attach `Decl *` to each `LateParsedAttribute *`.
@@ -6489,8 +6489,10 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
if (Kind == tok::star) {
DeclaratorChunk::LateAttrListTy OpaqueLateAttrList;
if (getLangOpts().ExperimentalLateParseAttributes && !LateAttrs.empty()) {
+ // TODO: Support `counted_by` in function parameters, return types, and
+ // other contexts (Issue #167365).
if (!D.isFunctionDeclarator()) {
- for (auto LA : LateAttrs) {
+ for (LateParsedAttribute *LA : LateAttrs) {
OpaqueLateAttrList.push_back(LA);
}
}
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 0c9ca303be1fd..e442c7ee73aaf 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,9 +102,6 @@ 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 {
struct size_known *__sized_by(size) buf;
@@ -156,7 +153,7 @@ struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
};
struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
- // expected-error at +1{{}}
+ // expected-error at +1{{'sized_by' attribute on nested pointer type is not allowed}}
void (* __sized_by(size) * fn_ptr)(void);
int size;
};
@@ -174,9 +171,7 @@ 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.
- // expected-error at +1{{}}
+ // expected-error at +1{{'sized_by' attribute on nested pointer type is not allowed}}
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 19e63d74de70b..db9791556b0c1 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
@@ -136,6 +136,7 @@ struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
};
struct on_member_pointer_fn_ty_ty_pos {
+ // 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;
>From 790bf43794ef750d44170af7bdfc52bc946dcb3d Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Mon, 10 Nov 2025 10:50:02 -0800
Subject: [PATCH 3/4] Clang format
---
clang/include/clang/Parse/Parser.h | 6 ++++--
clang/include/clang/Sema/DeclSpec.h | 7 ++++---
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index f2f969e7b2de1..b05badc24149b 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1164,8 +1164,10 @@ class Parser : public CodeCompletionHandler {
unsigned NestedTypeLevel;
explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
- SourceLocation Loc, unsigned NestedTypeLevel = 0)
- : Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(NestedTypeLevel) {}
+ SourceLocation Loc,
+ unsigned NestedTypeLevel = 0)
+ : Self(P), AttrName(Name), AttrNameLoc(Loc),
+ NestedTypeLevel(NestedTypeLevel) {}
void ParseLexedAttributes() override;
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index fd5ca9b0d089f..75a9e937adb52 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -2331,9 +2331,10 @@ 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,
- ArrayRef<DeclaratorChunk::LateAttrOpaquePtr> LateAttrs = {}) {
+ void
+ AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
+ SourceLocation EndLoc,
+ ArrayRef<DeclaratorChunk::LateAttrOpaquePtr> LateAttrs = {}) {
DeclTypeInfo.push_back(TI);
DeclTypeInfo.back().getAttrs().prepend(attrs.begin(), attrs.end());
getAttributePool().takeAllFrom(attrs.getPool());
>From 8673fd74d7f2be2f288137cb79a947495abe4b98 Mon Sep 17 00:00:00 2001
From: Yeoul Na <yeoul_na at apple.com>
Date: Mon, 10 Nov 2025 12:52:24 -0800
Subject: [PATCH 4/4] Fix test failures related to 'counted_by' on 'void *'
---
clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c | 3 ++-
.../Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
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 5242a1b04d892..dfca41ec107d4 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
@@ -123,7 +123,8 @@ struct on_member_pointer_const_incomplete_ty_ty_pos {
};
struct on_member_pointer_void_ty_ty_pos {
- // expected-error at +1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}}
+ // 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 *__counted_by(count) buf;
int count;
};
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 81a9d05077b45..7b09eca592e25 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
@@ -124,7 +124,8 @@ struct on_member_pointer_const_incomplete_ty_ty_pos {
};
struct on_member_pointer_void_ty_ty_pos {
- // expected-error at +1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}}
+ // 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 *__counted_by_or_null(count) buf;
int count;
};
More information about the cfe-commits
mailing list