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 <[email protected]> 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@+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@+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@+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@+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@+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@+2{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + // expected-error@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback1)(int *__counted_by(len1)); + // expected-error@+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@+1{{'sized_by' attribute cannot be applied to a parameter in a function pointer type}} + void (*callback3)(char *__sized_by(size1)); + // expected-error@+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@+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@+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@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+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@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+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@+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@+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@+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@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+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@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+1{{use of undeclared identifier 'count'}} + // expected-error@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+1{{'counted_by_or_null' only applies to pointers; did you mean to use 'counted_by'?}} + // expected-error@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+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@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+1{{}} struct size_known *__sized_by(size) *buf; int size; }; struct on_nested_pointer_outer { - // TODO: Allow this - // expected-error@+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@+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@+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@+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@+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@+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@+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@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+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@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+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@+1{{use of undeclared identifier 'size'}} + // TODO: Allow this + // expected-error@+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@+1{{use of undeclared identifier 'size'}} + // TODO: Allow this + // expected-error@+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@+1{{use of undeclared identifier 'size'}} + // expected-error@+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@+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@+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@+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@+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@+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@+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 <[email protected]> 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@+1{{}} + // expected-error@+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@+1{{}} + // expected-error@+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@+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 <[email protected]> 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 <[email protected]> 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@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}} + // expected-warning@+2{{'counted_by' on a pointer to void is a GNU extension, treated as 'sized_by'}} + // expected-note@+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@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}} + // expected-warning@+2{{'counted_by_or_null' on a pointer to void is a GNU extension, treated as 'sized_by_or_null'}} + // expected-note@+1{{use '__sized_by_or_null' to suppress this warning}} void *__counted_by_or_null(count) buf; int count; }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
