https://github.com/rapidsna created https://github.com/llvm/llvm-project/pull/224560
<sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub> >From 77371899a8fd67c22c5f67bc45f58b97998be831 Mon Sep 17 00:00:00 2001 From: Yeoul Na <[email protected]> Date: Fri, 18 Sep 2026 00:28:21 -0700 Subject: [PATCH 1/2] [BoundsSafety] Handle the counted_by family as a type attribute counted_by / sized_by (and their _or_null variants) were handled in only one way: a declaration-position attribute went through handleCountedByAttrField, which validated it and then patched the field afterwards with FieldDecl::setType. There was no type-position handling at all. Build the type during type construction instead, from a single handler that serves both positions: - Add HandleCountedByAttrOnType and dispatch the counted_by family to it from processTypeAttrs, going through the shared validateBoundsAttrTypeForTypePosition leaf. - Remove handleCountedByAttrField. Its FieldDecl-based type-shape checks in Sema::CheckCountedByAttrOnField are superseded by Sema::ValidateBoundsAttrTypeShape, added in the previous commit and now reached from the type path, and are deleted; no diagnostic is dropped. The checks that genuinely need the FieldDecl (union member, non-flexible array, cross-struct count) stay in CheckCountedByAttrOnField and run from ActOnFields. - BuildCountAttributedArrayOrPointerType accepts any array type -- the flexible-array-member check is deferred to CheckCountedByAttrOnField -- and rejects a non-DeclRefExpr count up front, where the diagnostic can still be attributed to the count expression. Building the node in type position is also what lets the next commit hand out an incomplete node and fill its count in later. Test expectations are updated in the next commit, which completes the change; this commit on its own leaves them stale. --- clang/lib/Sema/SemaBoundsSafety.cpp | 112 +--------------------------- clang/lib/Sema/SemaDecl.cpp | 25 ++++++- clang/lib/Sema/SemaDeclAttr.cpp | 44 ----------- clang/lib/Sema/SemaType.cpp | 70 ++++++++++++++++- 4 files changed, 96 insertions(+), 155 deletions(-) diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp index 2afe0812dcf4f..d92718b332ce6 100644 --- a/clang/lib/Sema/SemaBoundsSafety.cpp +++ b/clang/lib/Sema/SemaBoundsSafety.cpp @@ -173,20 +173,7 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, return true; } - const auto FieldTy = FD->getType(); - if (FieldTy->isArrayType() && (CountInBytes || OrNull)) { - Diag(FD->getBeginLoc(), - diag::err_count_attr_not_on_ptr_or_flexible_array_member) - << Kind << FD->getLocation() << /* suggest counted_by */ 1; - return true; - } - if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) { - Diag(FD->getBeginLoc(), - diag::err_count_attr_not_on_ptr_or_flexible_array_member) - << Kind << FD->getLocation() << /* do not suggest counted_by */ 0; - return true; - } - + const QualType FieldTy = FD->getType(); LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = LangOptions::StrictFlexArraysLevelKind::IncompleteOnly; if (FieldTy->isArrayType() && @@ -198,98 +185,7 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, return true; } - CountedByInvalidPointeeTypeKind InvalidTypeKind = - CountedByInvalidPointeeTypeKind::VALID; - QualType PointeeTy; - int SelectPtrOrArr = 0; - if (FieldTy->isPointerType()) { - PointeeTy = FieldTy->getPointeeType(); - SelectPtrOrArr = 0; - } else { - assert(FieldTy->isArrayType()); - const ArrayType *AT = getASTContext().getAsArrayType(FieldTy); - PointeeTy = AT->getElementType(); - SelectPtrOrArr = 1; - } - // Note: The `Decl::isFlexibleArrayMemberLike` check earlier on means - // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable - // when `FieldTy->isArrayType()`. - bool ShouldWarn = false; - if (!CountInBytes && PointeeTy->isAlwaysIncompleteType()) { - // In general using `counted_by` or `counted_by_or_null` on - // pointers where the pointee is an incomplete type are problematic. This is - // because it isn't possible to compute the pointer's bounds without knowing - // the pointee type size. At the same time it is common to forward declare - // types in header files. - // - // E.g.: - // - // struct Handle; - // struct Wrapper { - // size_t count; - // struct Handle* __counted_by(count) handles; - // } - // - // To allow the above code pattern but still prevent the pointee type from - // being incomplete in places where bounds checks are needed the following - // scheme is used: - // - // * When the pointee type might not always be an incomplete type (i.e. - // a type that is currently incomplete but might be completed later - // on in the translation unit) the attribute is allowed by this method - // but later uses of the FieldDecl are checked that the pointee type - // is complete see `BoundsSafetyCheckAssignmentToCountAttrPtr`, - // `BoundsSafetyCheckInitialization`, and - // `BoundsSafetyCheckUseOfCountAttrPtr` - // - // * When the pointee type is always an incomplete type (e.g. - // `void` in strict C mode) the attribute is disallowed by this method - // because we know the type can never be completed so there's no reason - // to allow it. - // - // Exception: void has an implicit size of 1 byte for pointer arithmetic - // (following GNU convention). Therefore, counted_by on void* is allowed - // and behaves equivalently to sized_by (treating the count as bytes). - bool IsVoidPtr = PointeeTy->isVoidType(); - if (IsVoidPtr) { - // Emit a warning that this is a GNU extension. - Diag(FD->getBeginLoc(), diag::ext_gnu_counted_by_void_ptr) << Kind; - Diag(FD->getBeginLoc(), diag::note_gnu_counted_by_void_ptr_use_sized_by) - << Kind; - assert(InvalidTypeKind == CountedByInvalidPointeeTypeKind::VALID); - } else { - InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE; - } - } else if (PointeeTy->isSizelessType()) { - InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS; - } else if (PointeeTy->isFunctionType()) { - InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION; - } else if (!CountInBytes && - PointeeTy->isStructureTypeWithFlexibleArrayMember()) { - if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) { - // This is a workaround for the Linux kernel that has already adopted - // `counted_by` on a FAM where the pointee is a struct with a FAM. This - // should be an error because computing the bounds of the array cannot be - // done correctly without manually traversing every struct object in the - // array at runtime. To allow the code to be built this error is - // downgraded to a warning. - ShouldWarn = true; - } - InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER; - } - - if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) { - unsigned DiagID = ShouldWarn - ? diag::warn_counted_by_attr_elt_type_unknown_size - : diag::err_counted_by_attr_pointee_unknown_size; - Diag(FD->getBeginLoc(), DiagID) - << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind - << (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange(); - return true; - } - - // Check the expression - + // Validate the expression type if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) { Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer) << Kind << E->getSourceRange(); @@ -304,6 +200,7 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, return true; } + // Validate count field references auto *CountDecl = DRE->getDecl(); FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl); if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) { @@ -325,9 +222,6 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, << Kind << CountFD->getSourceRange(); return true; } - // Whether CountRD is an anonymous struct is not determined at this - // point. Thus, an additional diagnostic in case it's not anonymous struct - // is done later in `Parser::ParseStructDeclaration`. auto *RD = GetEnclosingNamedOrTopAnonRecord(FD); auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 88f25903d97c3..ae40bc491aa5b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -17383,7 +17383,7 @@ void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D, // Always attach attributes to the underlying decl. if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) D = TD->getTemplatedDecl(); - ProcessDeclAttributeList(S, D, Attrs); + ProcessDeclAttributeList(S, D, Attrs, ProcessDeclAttributeOptions()); ProcessAPINotes(D); if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D)) @@ -20132,6 +20132,7 @@ bool Sema::EntirelyFunctionPointers(const RecordDecl *Record) { return llvm::all_of(Record->decls(), IsFunctionPointerOrForwardDecl); } + void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, ArrayRef<Decl *> Fields, SourceLocation LBrac, SourceLocation RBrac, @@ -20169,6 +20170,28 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, } } + if (!getLangOpts().ExperimentalLateParseAttributes) { + // Perform FieldDecl-dependent validation for counted_by family attributes. + for (auto *D : Fields) { + FieldDecl *FD = cast<FieldDecl>(D); + if (auto *CAT = FD->getType()->getAs<CountAttributedType>()) { + if (CheckCountedByAttrOnField(FD, CAT->getCountExpr(), + CAT->isCountInBytes(), + CAT->isOrNull())) { + // Rejected. Strip the CountAttributedType so the field keeps its + // plain wrapped type. The pre-refactor eager path built the type only + // after this check passed, so on failure no CAT ever existed; leaving + // it here would flow an invalid CAT downstream. Mirrors the late path + // in Sema::ActOnLateParsedTypeAttrArgument. + QualType Wrapped = CAT->desugar(); + FD->setType(Wrapped); + FD->setTypeSourceInfo( + Context.getTrivialTypeSourceInfo(Wrapped, FD->getLocation())); + } + } + } + } + // Verify that all the fields are okay. SmallVector<FieldDecl*, 32> RecFields; const FieldDecl *PreviousField = nullptr; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index eb4a8c2ab9ae0..533aafa938e66 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7069,43 +7069,6 @@ static void handleNoPFPAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { D->addAttr(NoFieldProtectionAttr::Create(S.Context, AL)); } -static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { - auto *CountExpr = AL.getArgAsExpr(0); - if (!CountExpr) - return; - - bool CountInBytes; - bool OrNull; - switch (AL.getKind()) { - case ParsedAttr::AT_CountedBy: - CountInBytes = false; - OrNull = false; - break; - case ParsedAttr::AT_CountedByOrNull: - CountInBytes = false; - OrNull = true; - break; - case ParsedAttr::AT_SizedBy: - CountInBytes = true; - OrNull = false; - break; - case ParsedAttr::AT_SizedByOrNull: - CountInBytes = true; - OrNull = true; - break; - default: - llvm_unreachable("unexpected counted_by family attribute"); - } - - FieldDecl *FD = cast<FieldDecl>(D); - if (S.CheckCountedByAttrOnField(FD, CountExpr, CountInBytes, OrNull)) - return; - - QualType CAT = S.BuildCountAttributedArrayOrPointerType( - FD->getType(), CountExpr, CountInBytes, OrNull); - FD->setType(CAT); -} - static void handleFunctionReturnThunksAttr(Sema &S, Decl *D, const ParsedAttr &AL) { StringRef KindStr; @@ -8200,13 +8163,6 @@ ProcessDeclAttribute(Sema &S, Decl *D, const ParsedAttr &AL, handleAvailableOnlyInDefaultEvalMethod(S, D, AL); break; - case ParsedAttr::AT_CountedBy: - case ParsedAttr::AT_CountedByOrNull: - case ParsedAttr::AT_SizedBy: - case ParsedAttr::AT_SizedByOrNull: - handleCountedByAttrField(S, D, AL); - break; - case ParsedAttr::AT_NoFieldProtection: handleNoPFPAttrField(S, D, AL); break; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index a634569a99711..f239e29e2c29d 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -6426,6 +6426,7 @@ GetTypeSourceInfoForDeclarator(TypeProcessingState &State, break; } + case TypeLoc::CountAttributed: case TypeLoc::Adjusted: case TypeLoc::BTFTagAttributed: { CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc(); @@ -9124,6 +9125,53 @@ static bool validateBoundsAttrTypeForTypePosition( return true; } +static void HandleCountedByAttrOnType(TypeProcessingState &State, + QualType &CurType, ParsedAttr &Attr) { + Sema &S = State.getSema(); + + // This attribute is only supported in C. + // FIXME: we should implement checkCommonAttributeFeatures() in SemaAttr.cpp + // such that it handles type attributes, and then call that from + // processTypeAttrs() instead of one-off checks like this. + if (!Attr.diagnoseLangOpts(S)) { + Attr.setInvalid(); + return; + } + + auto *CountExpr = Attr.getArgAsExpr(0); + if (!CountExpr) + return; + + // This is a mechanism to prevent nested count pointer types in the contexts + // where late parsing isn't allowed: currently that is any context other than + // struct fields. In the context where late parsing is allowed, the level + // check will be done once the whole context is constructed. + unsigned chunkIndex = State.getCurrentChunkIndex(); + unsigned pointerNestLevel = 0; + + // Only calculate pointer nest level if we're processing a declarator chunk. + // For DeclSpec attributes, the declarator hasn't been constructed yet. + if (chunkIndex > 0) { + pointerNestLevel = getPointerNestLevel(State, chunkIndex); + } + + Sema::BoundsAttrFlags Flags; + if (!validateBoundsAttrTypeForTypePosition(S, CurType, Attr.getKind(), + Attr.getLoc(), Attr.getRange(), + pointerNestLevel, Flags)) { + Attr.setInvalid(); + return; + } + + QualType NewType = S.BuildCountAttributedArrayOrPointerType( + CurType, CountExpr, Flags.CountInBytes, Flags.OrNull); + if (NewType.isNull()) { + Attr.setInvalid(); + return; + } + CurType = NewType; +} + bool Sema::ActOnLateParsedTypeAttr(ParsedAttr::Kind AttrKind, SourceLocation AttrNameLoc, QualType &type, unsigned pointerNestLevel, @@ -9372,6 +9420,14 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, break; } + case ParsedAttr::AT_CountedBy: + case ParsedAttr::AT_CountedByOrNull: + case ParsedAttr::AT_SizedBy: + case ParsedAttr::AT_SizedByOrNull: + HandleCountedByAttrOnType(state, type, attr); + attr.setUsedAsTypeAttr(); + break; + MS_TYPE_ATTRS_CASELIST: if (!handleMSPointerTypeQualifierAttr(state, attr, type)) attr.setUsedAsTypeAttr(); @@ -10163,7 +10219,19 @@ QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy, Expr *CountExpr, bool CountInBytes, bool OrNull) { - assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType()); + // Accept any array or pointer type here. For arrays, validation that it's + // a flexible array member is deferred until CheckCountedByAttrOnField. + assert(WrappedTy->isArrayType() || WrappedTy->isPointerType()); + + // Reject non-DeclRefExpr early to avoid cast failure in + // BuildTypeCoupledDecls. + if (!isa<DeclRefExpr>(CountExpr)) { + unsigned Kind = getCountAttrKind(CountInBytes, OrNull); + Diag(CountExpr->getBeginLoc(), + diag::err_count_attr_only_support_simple_decl_reference) + << Kind << CountExpr->getSourceRange(); + return QualType(); + } llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls; BuildTypeCoupledDecls(CountExpr, Decls); >From f9afbe6cf1a06a88a985cb1d2af63592ca6f8087 Mon Sep 17 00:00:00 2001 From: Yeoul Na <[email protected]> Date: Thu, 10 Sep 2026 07:40:37 -0700 Subject: [PATCH 2/2] [BoundsSafety] Create incomplete counted_by types and wire up the refill Activate late parsing for the counted_by family (counted_by / sized_by and their _or_null variants) in type-attribute position, under -fexperimental-late-parse-attributes, on top of the type-attribute handling, the validation helper and the refill machinery added in the previous commits. When such an attribute is seen during type construction and its argument can't be resolved yet, build the CountAttributedType immediately with getIncompleteCountAttributedType and record it against the enclosing record; its count expression is filled in at the closing brace via the refill logic. Because enclosing types refer to the node by pointer, completing it in place leaves the type chain untouched -- no rebuild, no TypeLoc re-emission. - Sema::ActOnLateParsedTypeAttr builds the incomplete node; the parser callback stores it on the LateParsedTypeAttribute and records the attribute in the record currently being parsed. Parser::CompleteLateParsedTypeAttributes drains that list at the closing brace; a nested anonymous record hands its pending attributes up to the enclosing record whose scope makes the argument visible. - A nested counted_by is diagnosed in the declarator-chunk loop, where the wrap would otherwise bury the CountAttributedType. - Free-function parameters have no enclosing record to complete them, so late parsing is gated to record members; parameters fall back to eager handling instead of leaving a null-count node to crash on PCH round-trip. Existing counted_by/sized_by tests are updated to the resolved-in-place AST and diagnostics, including the expectations left stale by the previous commit. --- clang/include/clang/AST/TypeBase.h | 2 +- clang/include/clang/Parse/Parser.h | 11 + clang/include/clang/Sema/Sema.h | 39 ++-- clang/lib/Parse/ParseDecl.cpp | 203 +++++++++++++++--- clang/lib/Sema/SemaType.cpp | 62 ++++++ .../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-counted-by-late-parsed-struct-ptrs.c | 79 +++---- .../Sema/attr-counted-by-or-null-last-field.c | 4 +- ...unted-by-or-null-late-parsed-struct-ptrs.c | 64 ++---- ...ruct-ptrs-completable-incomplete-pointee.c | 37 ++-- .../attr-counted-by-or-null-struct-ptrs.c | 14 +- ...ruct-ptrs-completable-incomplete-pointee.c | 32 ++- clang/test/Sema/attr-counted-by-struct-ptrs.c | 18 +- .../attr-sized-by-late-parsed-struct-ptrs.c | 52 +---- ...sized-by-or-null-late-parsed-struct-ptrs.c | 53 +---- .../Sema/attr-sized-by-or-null-struct-ptrs.c | 14 +- clang/test/Sema/attr-sized-by-struct-ptrs.c | 12 +- 20 files changed, 406 insertions(+), 387 deletions(-) diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 28f102fdaf534..454829ff58b43 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -3451,7 +3451,7 @@ class BoundsAttributedType : public Type, public llvm::FoldingSetNode { QualType WrappedTy; protected: - ArrayRef<TypeCoupledDeclRefInfo> Decls; // stored in trailing objects + ArrayRef<TypeCoupledDeclRefInfo> Decls; // allocated in the ASTContext BoundsAttributedType(TypeClass TC, QualType Wrapped, QualType Canon); diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 6d0affa3c8822..af0f9040185f2 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -292,6 +292,8 @@ class Parser : public CodeCompletionHandler { friend class PoisonSEHIdentifiersRAIIObject; friend class ParenBraceBracketBalancer; friend class BalancedDelimiterTracker; + friend struct LateParsedAttribute; + friend struct LateParsedTypeAttribute; Parser(Preprocessor &PP, Sema &Actions, bool SkipFunctionBodies); ~Parser() override; @@ -2246,6 +2248,8 @@ class Parser : public CodeCompletionHandler { ParsedAttributes Attrs(AttrFactory); ParseGNUAttributes(Attrs, LateAttrs, &D); D.takeAttributesAppending(Attrs); + if (LateAttrs) + Parser::TakeTypeAttrsAppendingFrom(D.getLateAttributes(), *LateAttrs); } } @@ -8172,6 +8176,13 @@ class Parser : public CodeCompletionHandler { QualType &type, unsigned pointerNestLevel); + /// The late-parsed type attributes of the record currently being parsed, so a + /// nested anonymous record can hand its unresolved attributes to the enclosing + /// record whose scope makes their arguments visible. Null outside a record + /// body. + SmallVectorImpl<LateParsedTypeAttribute *> *CurRecordLateParsedTypeAttrs = + nullptr; + /// We've parsed something that could plausibly be intended to be a template /// name (\p LHS) followed by a '<' token, and the following code can't /// possibly be an expression. Determine if this is likely to be a template-id diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f643f1cb328d8..952a15d029569 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -142,6 +142,8 @@ class InitializationKind; class InitializationSequence; class InitializedEntity; enum class LangAS : unsigned int; +struct LateParsedAttribute; +struct LateParsedTypeAttribute; class LocalInstantiationScope; class LookupResult; class MangleNumberingContext; @@ -2538,22 +2540,27 @@ class Sema final : public SemaBase { bool AllowRedecl = false, Expr *AttrArg = nullptr); - /// Check if applying the specified attribute variant from the "counted by" - /// family of attributes to FieldDecl \p FD is semantically valid. If - /// semantically invalid diagnostics will be emitted explaining the problems. - /// - /// \param FD The FieldDecl to apply the attribute to - /// \param E The count expression on the attribute - /// \param CountInBytes If true the attribute is from the "sized_by" family of - /// attributes. If the false the attribute is from - /// "counted_by" family of attributes. - /// \param OrNull If true the attribute is from the "_or_null" suffixed family - /// of attributes. If false the attribute does not have the - /// suffix. - /// - /// Together \p CountInBytes and \p OrNull decide the attribute variant. E.g. - /// \p CountInBytes and \p OrNull both being true indicates the - /// `counted_by_or_null` attribute. + /// Perform semantic validation on a FieldDecl with a "counted_by" family + /// attribute. This is called after the attribute has been attached to the + /// field's type (as a CountAttributedType) to validate the attribute is + /// correctly applied. + /// + /// This performs declaration-level checks that require the FieldDecl to + /// exist, complementing the type-level checks performed in + /// HandleCountedByAttrOnType during type processing. Specifically, this + /// validates: + /// - Field is not in a union + /// - For array fields, the field is a flexible array member + /// - Count expression is an integer type (not bool) + /// - Count expression references a field in the same struct + /// - Count field is not in a union + /// + /// \param FD The FieldDecl with the attribute + /// \param E The count expression from the attribute + /// \param CountInBytes If true the attribute is from the "sized_by" family. + /// If false the attribute is from the "counted_by" + /// family. + /// \param OrNull If true the attribute has the "_or_null" suffix. /// /// \returns false iff semantically valid. bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 6b121119550b1..fe2cfe8a5e641 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -34,6 +34,7 @@ #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/SaveAndRestore.h" #include <optional> using namespace clang; @@ -118,6 +119,23 @@ static bool IsAttributeArgsParsedInFunctionScope(const IdentifierInfo &II) { #undef CLANG_ATTR_PARSE_ARGS_IN_FUNCTION_SCOPE_LIST } +/// returns true iff the attribute appertains to a type (a TYPE_ATTR or +/// DECL_OR_TYPE_ATTR in `Attr.td`). +static bool IsAttributeTypeAttr(ParsedAttr::Kind Kind) { + switch (Kind) { +#define ATTR(NAME) +#define DECL_OR_TYPE_ATTR(NAME) case ParsedAttr::AT_##NAME: +#define TYPE_ATTR(NAME) case ParsedAttr::AT_##NAME: +#include "clang/Basic/AttrList.inc" + return true; + default: + return false; +#undef DECL_OR_TYPE_ATTR +#undef TYPE_ATTR +#undef ATTR + } +} + /// Check if the a start and end source location expand to the same macro. static bool FindLocsWithCommonFileID(Preprocessor &PP, SourceLocation StartLoc, SourceLocation EndLoc) { @@ -167,6 +185,9 @@ bool Parser::ParseSingleGNUAttribute(ParsedAttributes &Attrs, return false; } + ParsedAttr::Kind AttrKind = ParsedAttr::getParsedKind( + AttrName, nullptr, ParsedAttr::Form::GNU().getSyntax()); + bool LateParse = false; if (!LateAttrs) LateParse = false; @@ -175,7 +196,9 @@ bool Parser::ParseSingleGNUAttribute(ParsedAttributes &Attrs, // parsed for `LateAttrParseExperimentalExt` attributes. This will // only be late parsed if the experimental language option is enabled. LateParse = getLangOpts().ExperimentalLateParseAttributes && - IsAttributeLateParsedExperimentalExt(*AttrName); + IsAttributeLateParsedExperimentalExt(*AttrName) && + (IsAttributeTypeAttr(AttrKind) || + !LateAttrs->lateAttrParseTypeAttrOnly()); } else { // The caller did not restrict late parsing to only // `LateAttrParseExperimentalExt` attributes so late parse @@ -193,10 +216,24 @@ bool Parser::ParseSingleGNUAttribute(ParsedAttributes &Attrs, } // Handle attributes with arguments that require late parsing. - LateParsedAttribute *LA = - new LateParsedAttribute(this, *AttrName, AttrNameLoc); + // Late parsing for type attributes isn't properly supported in C++ yet. + LateParsedAttribute *LA = nullptr; + if (IsAttributeTypeAttr(AttrKind) && !getLangOpts().CPlusPlus) + LA = new LateParsedTypeAttribute(this, *AttrName, AttrNameLoc); + else + LA = new LateParsedAttribute(this, *AttrName, AttrNameLoc); + LateAttrs->push_back(LA); + // Record type attributes against the record currently being parsed, whose + // closing brace is when their arguments become resolvable. `LateAttrs` can't + // serve here: for a declarator-position attribute it is a transient local + // that is drained into a DeclaratorChunk, and for a decl-spec-position one + // TakeTypeAttrsAppendingFrom moves the entry into the DeclSpec. + if (auto *LTA = dyn_cast<LateParsedTypeAttribute>(LA); + LTA && CurRecordLateParsedTypeAttrs) + CurRecordLateParsedTypeAttrs->push_back(LTA); + // Attributes in a class are parsed at the end of the class, along // with other late-parsed declarations. if (!ClassStack.empty() && !LateAttrs->parseSoon()) @@ -3177,10 +3214,11 @@ void Parser::DistributeCLateParsedAttrs(Decl *Dcl, if (!LateAttrs) return; + // Attach `Decl *` to each `LateParsedAttribute *`. if (Dcl) { - for (auto *LateAttr : *LateAttrs) { - if (LateAttr->Decls.empty()) - LateAttr->addDecl(Dcl); + for (auto *LA : *LateAttrs) { + if (LA->Decls.empty()) + LA->addDecl(Dcl); } } } @@ -3253,12 +3291,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); } @@ -3498,6 +3530,10 @@ void Parser::ParseDeclarationSpecifiers( DS.takeAttributesAppendingingFrom(attrs); } + if (LateAttrs) { + Parser::TakeTypeAttrsAppendingFrom(DS.getLateAttributes(), *LateAttrs); + } + // If this is not a declaration specifier token, we're done reading decl // specifiers. First verify that DeclSpec's are consistent. DS.Finish(Actions, Policy); @@ -4030,7 +4066,6 @@ void Parser::ParseDeclarationSpecifiers( case tok::kw___declspec: ParseAttributes(PAKM_GNU | PAKM_Declspec, DS.getAttributes(), LateAttrs); continue; - // Microsoft single token adornments. case tok::kw___forceinline: { isInvalid = DS.setFunctionSpecForceInline(Loc, PrevSpec, DiagID); @@ -4782,8 +4817,20 @@ void Parser::ParseStructDeclaration( ParsedAttributes Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs); + // Late-parsed type attributes written in declaration-specifier position (e.g. + // `IP __counted_by(n) a, b;` where `IP` is a pointer typedef) belong to every + // declarator in this declaration, so remember where this declaration's entries + // start before the specifier list is parsed. Indices, not iterators: the side + // list is a SmallVector and only ever grows within a record body. + unsigned DeclSpecMark = + CurRecordLateParsedTypeAttrs ? CurRecordLateParsedTypeAttrs->size() : 0; + // Parse the common specifier-qualifiers-list piece. - ParseSpecifierQualifierList(DS); + ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_normal, + LateFieldAttrs); + + unsigned AfterDeclSpecMark = + CurRecordLateParsedTypeAttrs ? CurRecordLateParsedTypeAttrs->size() : 0; // If there are no declarators, this is a free-standing declaration // specifier. Let the actions module cope with it. @@ -4819,6 +4866,8 @@ void Parser::ParseStructDeclaration( /// struct-declarator: declarator /// struct-declarator: declarator[opt] ':' constant-expression + unsigned DeclMark = + CurRecordLateParsedTypeAttrs ? CurRecordLateParsedTypeAttrs->size() : 0; if (Tok.isNot(tok::colon)) { // Don't parse FOO:BAR as if it were a typo for FOO::BAR. ColonProtectionRAIIObject X(*this); @@ -4847,6 +4896,31 @@ void Parser::ParseStructDeclaration( if (Field) DistributeCLateParsedAttrs(Field, LateFieldAttrs); + // Record the field each pending late-parsed type attribute belongs to, in + // the base class's coupled-decl list. The callback above ran + // GetTypeForDeclarator, so the attribute's type node exists by now; pairing + // it with the field here means the completion pass at the closing brace + // needs no search -- which matters because a bounds type may sit nested + // inside the field's type, where it cannot be recovered by inspecting the + // field's top-level type. + // + // Two ranges apply: attributes from the shared declaration-specifier (every + // declarator in this declaration gets appended), and those from this + // declarator alone. + if (auto *FD = dyn_cast_if_present<FieldDecl>(Field); + FD && CurRecordLateParsedTypeAttrs) { + unsigned Size = CurRecordLateParsedTypeAttrs->size(); + assert(Size >= DeclMark && DeclMark >= AfterDeclSpecMark && + AfterDeclSpecMark >= DeclSpecMark && + "late-parsed type attribute list must only grow"); + auto Attach = [&](unsigned First, unsigned Last) { + for (unsigned I = First; I != Last; ++I) + (*CurRecordLateParsedTypeAttrs)[I]->addDecl(FD); + }; + Attach(DeclSpecMark, AfterDeclSpecMark); + Attach(DeclMark, Size); + } + // If we don't have a comma, it is either the end of the list (a ';') // or an error, bail out. if (!TryConsumeToken(tok::comma, CommaLoc)) @@ -4897,9 +4971,6 @@ ParsedAttributes Parser::ParseLexedAttributeTokens(LateParsedAttribute &LPA) { void Parser::ParseLexedTypeAttribute(LateParsedTypeAttribute &LA, ParsedAttributes &OutAttrs) { - assert(LA.Decls.size() <= 1 && - "late field attribute expects to have at most one declaration."); - ParsedAttributes Attrs = ParseLexedAttributeTokens(LA); OutAttrs.takeAllAppendingFrom(Attrs); } @@ -5020,6 +5091,14 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, LateParsedAttrList LateFieldAttrs(/*PSoon=*/true, /*LateAttrParseExperimentalExtOnly=*/true); + // Pending late-parsed type attributes for this record, populated as its + // fields are parsed and drained at the closing brace. Exposed to nested + // bodies so an anonymous nested record can hand its own up to us; + // `Enclosing.get()` is our caller's list, or null for the outermost record. + SmallVector<LateParsedTypeAttribute *, 2> LateTypeAttrs; + llvm::SaveAndRestore<SmallVectorImpl<LateParsedTypeAttribute *> *> Enclosing( + CurRecordLateParsedTypeAttrs, &LateTypeAttrs); + // While we still have something to read, read the declarations in the struct. while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { @@ -5126,15 +5205,49 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, ParsedAttributes attrs(AttrFactory); // If attributes exist after struct contents, parse them. MaybeParseGNUAttributes(attrs, &LateFieldAttrs); - SmallVector<Decl *, 32> FieldDecls(TagDecl->fields()); Actions.ActOnFields(getCurScope(), RecordLoc, TagDecl, FieldDecls, T.getOpenLocation(), T.getCloseLocation(), attrs); // Late parse field attributes if necessary. + // + // Late-parsed type attributes are owned by CompleteLateParsedTypeAttributes + // via the record's side list, which parses their tokens and deletes them. + // They are only in this generic list to be routed to type construction; if + // any remain (e.g. a type attribute that wasn't moved into a DeclSpec / + // DeclaratorChunk), drop them here so ParseLexedAttributeList doesn't parse + // and free them a second time. + llvm::erase_if(LateFieldAttrs, [](LateParsedAttribute *LA) { + return isa<LateParsedTypeAttribute>(LA); + }); ParseLexedAttributeList(LateFieldAttrs, /*D=*/nullptr, /*EnterScope=*/false, /*OnDefinition=*/false); + + // Resolve late-parsed type attributes while this record's fields are still in + // scope. A truly anonymous record can't do that yet — its count may live in + // the enclosing record and only becomes visible once its members are + // flattened in — so it hands its pending attributes up instead. + // + // `isAnonymousStructOrUnion()` isn't set until the enclosing context sees + // whether a declarator follows, which happens after we return. Determine it + // the way the parser can: no tag name and no declarator after the body. Any + // attribute-specifiers between `}` and the `;`/declarator are skipped with a + // reverting tentative parse, so a trailing `[[...]]` / `__attribute__` etc. + // doesn't defeat the check. + if (getLangOpts().ExperimentalLateParseAttributes && + !LateTypeAttrs.empty()) { + bool IsAnonymous = false; + if (!TagDecl->getIdentifier()) { + TentativeParsingAction TPA(*this); + IsAnonymous = TrySkipAttributes() && Tok.is(tok::semi); + TPA.Revert(); + } + if (IsAnonymous && Enclosing.get()) + llvm::append_range(*Enclosing.get(), LateTypeAttrs); + else + CompleteLateParsedTypeAttributes(LateTypeAttrs); + } StructScope.Exit(); Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, T.getRange()); } @@ -6467,7 +6580,9 @@ void Parser::ParseTypeQualifierListOpt( // recovery is graceful. if (AttrReqs & AR_GNUAttributesParsed || AttrReqs & AR_GNUAttributesParsedAndRejected) { - ParseGNUAttributes(DS.getAttributes()); + + // FIXME: Late parse only when some flag is set. + ParseGNUAttributes(DS.getAttributes(), LateAttrs); continue; // do *not* consume the next token! } // otherwise, FALL THROUGH! @@ -6627,6 +6742,8 @@ void Parser::ParseDeclaratorInternal(Declarator &D, DeclSpec DS(AttrFactory); ParseTypeQualifierListOpt(DS); + assert(DS.getLateAttributes().empty()); + D.AddTypeInfo( DeclaratorChunk::getPipe(DS.getTypeQualifiers(), DS.getPipeLoc()), std::move(DS.getAttributes()), SourceLocation()); @@ -6654,26 +6771,48 @@ void Parser::ParseDeclaratorInternal(Declarator &D, ((D.getContext() != DeclaratorContext::CXXNew) ? AR_GNUAttributesParsed : AR_GNUAttributesParsedAndRejected); + + // Late-parsed type attributes apply to members and function parameters, + // not variables. Completion is driven by the enclosing record + // (CompleteLateParsedTypeAttributes), so only late-parse when there is one: + // a free-function prototype (e.g. `void f(int *__counted_by(n), int n)`) + // has no record to complete into, and late-parsing there would leave a + // CountAttributedType with a null count in the AST. Such parameters fall + // back to eager handling instead. A function-pointer parameter inside a + // struct field is still late-parsed, since that record completes it. + bool LateParsingContext = (D.getContext() == DeclaratorContext::Member || + D.getContext() == DeclaratorContext::Prototype) && + CurRecordLateParsedTypeAttrs != nullptr; + + // No guard on ExperimentalLateParseAttributes is needed here; + // DS.getLateAttributes() already initializes with + // LateAttrParseExperimentalExtOnly. + LateParsedAttrList *LateAttrs = + LateParsingContext ? &DS.getLateAttributes() : nullptr; + ParseTypeQualifierListOpt(DS, Reqs, /*AtomicOrPtrauthAllowed=*/true, - !D.mayOmitIdentifier()); + !D.mayOmitIdentifier(), {}, LateAttrs); D.ExtendWithDeclSpec(DS); // Recursively parse the declarator. Actions.runWithSufficientStackSpace( D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); }); - if (Kind == tok::star) + if (Kind == tok::star) { // Remember that we parsed a pointer type, and remember the type-quals. D.AddTypeInfo(DeclaratorChunk::getPointer( DS.getTypeQualifiers(), Loc, DS.getConstSpecLoc(), DS.getVolatileSpecLoc(), DS.getRestrictSpecLoc(), DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc(), DS.getOverflowBehaviorLoc(), DS.isWrapSpecified()), - std::move(DS.getAttributes()), SourceLocation()); - else + std::move(DS.getAttributes()), SourceLocation(), + std::move(DS.getLateAttributes())); + } else { + assert(DS.getLateAttributes().empty()); // Remember that we parsed a Block type, and remember the type-quals. D.AddTypeInfo( DeclaratorChunk::getBlockPointer(DS.getTypeQualifiers(), Loc), std::move(DS.getAttributes()), SourceLocation()); + } } else { // Is a reference DeclSpec DS(AttrFactory); @@ -6724,6 +6863,8 @@ void Parser::ParseDeclaratorInternal(Declarator &D, } } + assert(DS.getLateAttributes().empty()); + // Remember that we parsed a reference type. D.AddTypeInfo(DeclaratorChunk::getReference(DS.getTypeQualifiers(), Loc, Kind == tok::amp), @@ -7237,9 +7378,18 @@ void Parser::ParseParenDeclarator(Declarator &D) { // sort of paren this is. // ParsedAttributes attrs(AttrFactory); + LateParsedAttrList LateAttrs(true, true, true); bool RequiresArg = false; if (Tok.is(tok::kw___attribute)) { - ParseGNUAttributes(attrs); + // Only late-parse type attributes when there is an enclosing record to + // complete the CountAttributedType (see ParseDeclaratorInternal). A + // grouping paren at file scope, e.g. `IP (__counted_by(n) x)` where `IP` is + // a pointer typedef, has no record to complete into, so late-parsing there + // would leave a CountAttributedType with a null count in the AST; parse + // eagerly instead. + LateParsedAttrList *LA = + CurRecordLateParsedTypeAttrs ? &LateAttrs : nullptr; + ParseGNUAttributes(attrs, LA); // We require that the argument list (if this is a non-grouping paren) be // present even if the attribute list was empty. @@ -7294,7 +7444,7 @@ void Parser::ParseParenDeclarator(Declarator &D) { T.consumeClose(); D.AddTypeInfo( DeclaratorChunk::getParen(T.getOpenLocation(), T.getCloseLocation()), - std::move(attrs), T.getCloseLocation()); + std::move(attrs), T.getCloseLocation(), LateAttrs); D.setGroupingParens(hadGroupingParens); @@ -7305,6 +7455,9 @@ void Parser::ParseParenDeclarator(Declarator &D) { return; } + assert(LateAttrs.empty() && + "Late parsed type attribute on FirstParamAttr is dropped"); + // Okay, if this wasn't a grouping paren, it must be the start of a function // argument list. Recognize that this declarator will never have an // identifier (and remember where it would have been), then call into diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index f239e29e2c29d..5328131654e5e 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -409,6 +409,14 @@ processTypeAttrs(TypeProcessingState &state, QualType &type, TypeAttrLocation TAL, const ParsedAttributesView &attrs, CUDAFunctionTarget CFT = CUDAFunctionTarget::HostDevice); +static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type, + const LateParsedAttrList &LateAttrs, + unsigned chunkIndex = 0); + +static void +BuildTypeCoupledDecls(Expr *E, + llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls); + static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr, QualType &type, CUDAFunctionTarget CFT); @@ -1510,6 +1518,9 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // are never distributed. processTypeAttrs(state, Result, TAL_DeclSpec, SlidingAttrs); processTypeAttrs(state, Result, TAL_DeclSpec, DS.getAttributes()); + + // Process the late attributes that appeared after the type name + processLateTypeAttrs(state, Result, DS.getLateAttributes()); } // Apply const/volatile/restrict qualifiers to T. @@ -4720,6 +4731,29 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, state.setCurrentChunkIndex(chunkIndex); DeclaratorChunk &DeclType = D.getTypeObject(chunkIndex); IsQualifiedFunction &= DeclType.Kind == DeclaratorChunk::Paren; + + // A counted_by-family attribute has to end up at the outermost level of the + // declared type. `int *__counted_by(n) *p` would bury the + // CountAttributedType under another pointer, where the bounds can't be + // maintained, so diagnose as soon as a chunk is about to wrap one. Only + // reachable for late-parsed attributes, since the eager path applies the + // attribute after the declarator is built. + if (DeclType.Kind == DeclaratorChunk::Pointer || + DeclType.Kind == DeclaratorChunk::Array) { + if (const auto *CATy = T->getAs<CountAttributedType>()) { + // A counted_by-family attribute buried under another pointer or array + // can't maintain its bounds. Diagnose it and drop it to its wrapped + // type -- matching the eager path, which drops the attribute rather than + // applying it. Because this fires just before the enclosing chunk wraps + // the node, replacing T here needs no enclosing-type rebuild. The + // node is left orphaned; the completion pass detects and skips it (see + // Sema::ActOnLateParsedTypeAttrArgument). + S.Diag(DeclType.Loc, diag::err_counted_by_on_nested_pointer) + << CATy->getKind(); + T = CATy->desugar(); + } + } + switch (DeclType.Kind) { case DeclaratorChunk::Paren: if (i == 0) @@ -5487,6 +5521,14 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, processTypeAttrs(state, T, TAL_DeclChunk, DeclType.getAttrs(), S.CUDA().IdentifyTarget(D.getAttributes())); + // The pointer-nest-level check for late-parsed attributes is intentionally + // deferred: at this point the enclosing chunks have not all been applied, + // so it is done once the whole declarator is built, when a Pointer/Array + // chunk is seen wrapping a CountAttributedType (see the + // err_counted_by_on_nested_pointer diagnostic above). processLateTypeAttrs + // therefore passes pointerNestLevel == 0 here. + processLateTypeAttrs(state, T, DeclType.LateAttrList, chunkIndex); + if (DeclType.Kind != DeclaratorChunk::Paren) { if (ExpectNoDerefChunk && !IsNoDerefableChunk(DeclType)) S.Diag(DeclType.Loc, diag::warn_noderef_on_non_pointer_or_array); @@ -5667,6 +5709,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, processTypeAttrs(state, T, TAL_DeclName, NonSlidingAttrs); processTypeAttrs(state, T, TAL_DeclName, D.getAttributes()); + processLateTypeAttrs(state, T, D.getLateAttributes()); + // Diagnose any ignored type attributes. state.diagnoseIgnoredTypeAttrs(T); @@ -9192,6 +9236,24 @@ bool Sema::ActOnLateParsedTypeAttr(ParsedAttr::Kind AttrKind, *BATy = CATy; return true; } +static bool processLateTypeAttrs(TypeProcessingState &state, QualType &type, + const LateParsedAttrList &LateAttrs, + unsigned chunkIndex) { + + if (LateAttrs.empty()) + return true; + + Sema &S = state.getSema(); + unsigned pointerNestLevel = 0; + + assert(S.ProcessLateParsedTypeAttrCallback); + + for (auto *LA : LateAttrs) + if (!S.ProcessLateParsedTypeAttrCallback(LA, type, pointerNestLevel)) + return false; + + return true; +} static void processTypeAttrs(TypeProcessingState &state, QualType &type, TypeAttrLocation TAL, 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-counted-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c index 443ccbbae66db..554bcfbd8d5c7 100644 --- a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c @@ -29,9 +29,8 @@ struct on_member_pointer_const_incomplete_ty { }; struct on_member_pointer_void_ty { - // 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* buf __counted_by(count); + // expected-warning@+1{{'counted_by' on a pointer to void is a GNU extension, treated as 'sized_by'}} + void* buf __counted_by(count); // expected-note{{use '__sized_by' to suppress this warning}} int count; }; @@ -88,9 +87,7 @@ struct on_member_pointer_struct_with_annotated_vla { }; struct on_pointer_anon_buf { - // TODO: Support referring to parent scope struct { - // expected-error@+1{{use of undeclared identifier 'count'}} struct size_known *buf __counted_by(count); }; int count; @@ -106,131 +103,113 @@ 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'}} - void *__counted_by(count) buf; + // expected-warning@+1{{'counted_by' on a pointer to void is a GNU extension, treated as 'sized_by'}} + void *__counted_by(count) buf; // expected-note{{use '__sized_by' to suppress this warning}} int count; }; // - struct on_member_pointer_fn_ptr_ty_pos { - // TODO: buffer of `count` function pointers should be allowed - // but fails because this isn't late parsed. - // expected-error@+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' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} void (* __counted_by(count) * fn_ptr)(void); int count; }; +struct on_member_ptr_ptr_fn_ptr_ty_ty_pos_inner { + // expected-error@+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; }; +struct on_nested_pointer_array_inner { + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + struct size_known *__counted_by(count) arr[10]; + int count; +}; + +struct on_nested_pointer_flexible_array_inner { + // expected-error@+2{{flexible array member 'arr' with type 'struct size_known *[]' is not at the end of struct}} + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + struct size_known *__counted_by(count) arr[]; + int count; // expected-note{{next field declaration is here}} +}; + struct on_pointer_anon_buf_ty_pos { struct { - // TODO: Support referring to parent scope - // expected-error@+1{{use of undeclared identifier 'count'}} struct size_known * __counted_by(count) buf; }; int count; }; 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-last-field.c b/clang/test/Sema/attr-counted-by-or-null-last-field.c index d0c50a733acef..9a1cae59f5282 100644 --- a/clang/test/Sema/attr-counted-by-or-null-last-field.c +++ b/clang/test/Sema/attr-counted-by-or-null-last-field.c @@ -128,9 +128,7 @@ struct on_member_ptr_incomplete_const_ty_ty_pos { struct on_member_ptr_void_ty_ty_pos { int count; - // expected-warning@+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 * ptr __counted_by_or_null(count); + void * ptr __counted_by_or_null(count); // expected-warning{{'counted_by_or_null' on a pointer to void is a GNU extension, treated as 'sized_by_or_null'}} expected-note{{use '__sized_by_or_null' to suppress this warning}} }; typedef void(fn_ty)(int); diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c index 233b729f87ccd..9f91f66b6e1c4 100644 --- a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c @@ -30,9 +30,7 @@ struct on_member_pointer_const_incomplete_ty { }; struct on_member_pointer_void_ty { - // 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* buf __counted_by_or_null(count); + void* buf __counted_by_or_null(count); // expected-warning{{'counted_by_or_null' on a pointer to void is a GNU extension, treated as 'sized_by_or_null'}} expected-note{{use '__sized_by_or_null' to suppress this warning}} int count; }; @@ -89,9 +87,7 @@ struct on_member_pointer_struct_with_annotated_vla { }; struct on_pointer_anon_buf { - // TODO: Support referring to parent scope struct { - // expected-error@+1{{use of undeclared identifier 'count'}} struct size_known *buf __counted_by_or_null(count); }; int count; @@ -107,131 +103,99 @@ 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'}} - void *__counted_by_or_null(count) buf; + void *__counted_by_or_null(count) buf; // expected-warning{{'counted_by_or_null' on a pointer to void is a GNU extension, treated as 'sized_by_or_null'}} expected-note{{use '__sized_by_or_null' to suppress this warning}} int count; }; // - struct on_member_pointer_fn_ptr_ty_pos { - // TODO: buffer of `count` function pointers should be allowed - // but fails because this isn't late parsed. - // expected-error@+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{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} void (* __counted_by_or_null(count) * fn_ptr)(void); int count; }; +struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner { + // expected-error@+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; }; struct on_pointer_anon_buf_ty_pos { struct { - // TODO: Support referring to parent scope - // expected-error@+1{{use of undeclared identifier 'count'}} struct size_known * __counted_by_or_null(count) buf; }; int count; }; 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..4d4a6e81e3766 100644 --- a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c +++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c @@ -17,7 +17,7 @@ // expected-note@+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,16 +616,13 @@ struct IncompleteTy3; struct CBBufFAMofCountedByPtrs { int size; - // TODO: This is misleading. The attribute is written in the type position - // but clang currently doesn't treat it like that and it gets treated as - // an attribute on the array, rather than on the element type. - // expected-error@+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[]; }; void arr_of_counted_by_ptr(struct CBBufFAMofCountedByPtrs* ptr) { - // TODO: Should be disallowed once parsing attributes in the type position - // works. + // TODO: Diagnostic should appear here once `__counted_by_or_null` is allowed on + // nested pointers. ptr->arr[0] = 0x0; void* addr = ((char*) ptr->arr[0]) + 1; } diff --git a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c index 0fd739ca7d4c3..e8ab29cd80181 100644 --- a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c @@ -105,8 +105,6 @@ struct on_pointer_anon_count { //============================================================================== // __counted_by_or_null on struct member pointer in type attribute position //============================================================================== -// TODO: Correctly parse counted_by_or_null as a type attribute. Currently it is parsed -// as a declaration attribute struct on_member_pointer_complete_ty_ty_pos { int count; @@ -158,13 +156,18 @@ struct on_member_pointer_fn_ptr_ty_ty_pos { fn_ptr_ty __counted_by_or_null(count) fn_ptr; }; -// TODO: This should be forbidden but isn't due to counted_by_or_null being treated -// as a declaration attribute. struct on_member_pointer_fn_ptr_ty_ty_pos_inner { int count; + // expected-error@+1{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} void (* __counted_by_or_null(count) * fn_ptr)(void); }; +struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner { + int count; + // expected-error@+1{{'counted_by_or_null' attribute on nested pointer type is not allowed}} + void (** __counted_by_or_null(count) * fn_ptr)(void); +}; + struct on_member_pointer_struct_with_vla_ty_pos { int count; // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}} @@ -181,9 +184,8 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos { }; struct on_nested_pointer_inner { - // TODO: This should be disallowed because in the `-fbounds-safety` model - // `__counted_by_or_null` can only be nested when used in function parameters. int count; + // expected-error@+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..f8957ca8b4eba 100644 --- a/clang/test/Sema/attr-counted-by-struct-ptrs.c +++ b/clang/test/Sema/attr-counted-by-struct-ptrs.c @@ -104,8 +104,6 @@ struct on_pointer_anon_count { //============================================================================== // __counted_by on struct member pointer in type attribute position //============================================================================== -// TODO: Correctly parse counted_by as a type attribute. Currently it is parsed -// as a declaration attribute struct on_member_pointer_complete_ty_ty_pos { int count; @@ -157,11 +155,16 @@ struct on_member_pointer_fn_ptr_ty_ty_pos { fn_ptr_ty __counted_by(count) fn_ptr; }; -// TODO: This should be forbidden but isn't due to counted_by being treated -// as a declaration attribute. struct on_member_pointer_fn_ptr_ty_ty_pos_inner { int count; - void (* __counted_by(count) * fn_ptr)(void); + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} + void (* __counted_by(count) * fn_ptr)(void); // FIXME +}; + +struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner { + int count; + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + void (** __counted_by(count) * fn_ptr)(void); }; struct on_member_pointer_struct_with_vla_ty_pos { @@ -180,10 +183,9 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos { }; struct on_nested_pointer_inner { - // TODO: This should be disallowed because in the `-fbounds-safety` model - // `__counted_by` can only be nested when used in function parameters. int count; - struct size_known *__counted_by(count) *buf; + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + struct size_known *__counted_by(count) *buf; // FIXME }; struct on_nested_pointer_outer { diff --git a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c index cfdf3407332c9..054fa0fec51e1 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 @@ -82,9 +82,7 @@ struct on_member_pointer_struct_with_annotated_vla { }; struct on_pointer_anon_buf { - // TODO: Support referring to parent scope struct { - // expected-error@+1{{use of undeclared identifier 'size'}} struct size_known *buf __sized_by(size); }; int size; @@ -100,35 +98,23 @@ struct on_pointer_anon_count { //============================================================================== // __sized_by on struct member pointer in type attribute position //============================================================================== -// TODO: Correctly parse sized_by as a type attribute. Currently it is parsed -// as a declaration attribute and is **not** late parsed resulting in the `size` -// field being unavailable. struct on_member_pointer_complete_ty_ty_pos { - // TODO: Allow this - // expected-error@+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; }; @@ -136,91 +122,73 @@ 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{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} void (* __sized_by(size) * fn_ptr)(void); int size; }; +struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner { + // expected-error@+1{{'sized_by' attribute on nested pointer type is not allowed}} + void (** __sized_by(size) * fn_ptr)(void); + int size; +}; + struct on_member_pointer_struct_with_vla_ty_pos { - // TODO: This should be allowed with sized_by. - // expected-error@+1{{use of undeclared identifier 'size'}} 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'}} struct has_annotated_vla* __sized_by(size) objects; int size; }; struct on_nested_pointer_inner { - // TODO: This should be disallowed because in the `-fbounds-safety` model - // `__sized_by` can only be nested when used in function parameters. - // expected-error@+1{{use of undeclared identifier 'size'}} + // expected-error@+1{{'sized_by' attribute on nested pointer type is not allowed}} struct size_known *__sized_by(size) *buf; int size; }; struct on_nested_pointer_outer { - // TODO: Allow this - // expected-error@+1{{use of undeclared identifier 'size'}} struct size_known **__sized_by(size) buf; int size; }; struct on_pointer_anon_buf_ty_pos { struct { - // TODO: Support referring to parent scope - // expected-error@+1{{use of undeclared identifier 'size'}} struct size_known * __sized_by(size) buf; }; int size; }; 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 b726a9b005b9b..50d5f57ded023 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 @@ -82,9 +82,7 @@ struct on_member_pointer_struct_with_annotated_vla { }; struct on_pointer_anon_buf { - // TODO: Support referring to parent scope struct { - // expected-error@+1{{use of undeclared identifier 'size'}} struct size_known *buf __sized_by_or_null(size); }; int size; @@ -100,35 +98,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; }; @@ -136,91 +122,74 @@ 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'}} + // 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; }; 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{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} void (* __sized_by_or_null(size) * fn_ptr)(void); int size; }; +struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner { + // expected-error@+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'}} 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'}} 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; }; struct on_pointer_anon_buf_ty_pos { struct { - // TODO: Support referring to parent scope - // expected-error@+1{{use of undeclared identifier 'size'}} struct size_known * __sized_by_or_null(size) buf; }; int size; }; 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 82819e5cf43ff..69ce8c5a67c70 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 @@ -100,8 +100,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; @@ -149,13 +147,18 @@ struct on_member_pointer_fn_ptr_ty_ty_pos { fn_ptr_ty __sized_by_or_null(size) fn_ptr; }; -// TODO: This should be forbidden but isn't due to sized_by_or_null being treated -// as a declaration attribute. struct on_member_pointer_fn_ptr_ty_ty_pos_inner { int size; + // expected-error@+1{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} void (* __sized_by_or_null(size) * fn_ptr)(void); }; +struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner { + int size; + // expected-error@+1{{'sized_by_or_null' attribute on nested pointer type is not allowed}} + void (** __sized_by_or_null(size) * fn_ptr)(void); +}; + struct on_member_pointer_struct_with_vla_ty_pos { int size; struct has_unannotated_vla *__sized_by_or_null(size) objects; @@ -167,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_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 6dc76b8bc7081..d56598f012fc2 100644 --- a/clang/test/Sema/attr-sized-by-struct-ptrs.c +++ b/clang/test/Sema/attr-sized-by-struct-ptrs.c @@ -149,13 +149,18 @@ struct on_member_pointer_fn_ptr_ty_ty_pos { fn_ptr_ty __sized_by(size) fn_ptr; }; -// TODO: This should be forbidden but isn't due to sized_by being treated -// as a declaration attribute. struct on_member_pointer_fn_ptr_ty_ty_pos_inner { int size; + // expected-error@+1{{cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} void (* __sized_by(size) * fn_ptr)(void); }; +struct on_member_pointer_fn_ptr_ty_ty_ty_pos_inner { + int size; + // expected-error@+1{{'sized_by' attribute on nested pointer type is not allowed}} + void (** __sized_by(size) * fn_ptr)(void); +}; + struct on_member_pointer_struct_with_vla_ty_pos { int size; struct has_unannotated_vla *__sized_by(size) objects; @@ -167,9 +172,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; }; _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
