https://github.com/rapidsna created https://github.com/llvm/llvm-project/pull/166491
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 >From f0ec14089464ef30a08f9d0646a3b0b7a4fdf5f0 Mon Sep 17 00:00:00 2001 From: Yeoul Na <[email protected]> Date: Wed, 22 Oct 2025 10:46:46 -0700 Subject: [PATCH] [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 fa509536bf021..98b470a525d11 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 c67ed99b1f49e..becadbb1a39d6 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 7e4a164e34eda..514f6daf44e2f 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. @@ -6124,7 +6146,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); @@ -6266,7 +6289,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! @@ -6447,21 +6472,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 39ab13653f5fe..8411eec7dfb3f 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 fc3aabf5741ca..85e75d4e4b91a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16873,11 +16873,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 8d4e0c510603a..42974c7d9d45f 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 @@ -104,35 +104,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; }; @@ -140,79 +129,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; }; @@ -227,8 +194,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 2150c81f9e9be..96169e6fbd986 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 @@ -105,35 +105,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; }; @@ -141,79 +130,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; }; @@ -228,8 +195,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 0bb09059c97f9..36ece813defa9 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 @@ -104,8 +104,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; @@ -156,10 +154,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); }; @@ -179,9 +176,8 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos { }; struct on_nested_pointer_inner { - // TODO: This should be disallowed because in the `-fbounds-safety` model - // `__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 c05d18262e2b7..1dd549b47cea9 100644 --- a/clang/test/Sema/attr-counted-by-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-struct-ptrs.c @@ -103,8 +103,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; @@ -155,10 +153,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); }; @@ -178,9 +175,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; }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
