diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 81e9d0423f96a..5d59b3b053600 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -317,7 +317,8 @@ New Compiler Flags - ``-fexperimental-late-parse-attributes`` enables an experimental feature to allow late parsing certain attributes in specific contexts where they would - not normally be late parsed. + not normally be late parsed. Currently this allows late parsing the + `counted_by` attribute in C. See `Attribute Changes in Clang`_. - ``-fseparate-named-sections`` uses separate unique sections for global symbols in named special sections (i.e. symbols annotated with @@ -406,6 +407,24 @@ Attribute Changes in Clang - The ``clspv_libclc_builtin`` attribute has been added to allow clspv (`OpenCL-C to Vulkan SPIR-V compiler `_) to identify functions coming from libclc (`OpenCL-C builtin library `_). +- The ``counted_by`` attribute is now allowed on pointers that are members of a + struct in C. + +- The ``counted_by`` attribute can now be late parsed in C when + ``-fexperimental-late-parse-attributes`` is passed but only when attribute is + used in the declaration attribute position. This allows using the + attribute on existing code where it previously impossible to do so without + re-ordering struct field declarations would break ABI as shown below. + + .. code-block:: c + + struct BufferTy { + /* Refering to `count` requires late parsing */ + char* buffer __counted_by(count); + /* Swapping `buffer` and `count` to avoid late parsing would break ABI */ + size_t count; + }; + Improvements to Clang's diagnostics ----------------------------------- diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 9a5c6e8d562c3..263b632df23ce 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2515,6 +2515,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isRecordType() const; bool isClassType() const; bool isStructureType() const; + bool isStructureTypeWithFlexibleArrayMember() const; bool isObjCBoxableRecordType() const; bool isInterfaceType() const; bool isStructureOrClassType() const; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 7008bea483c87..565708cef28ce 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2256,7 +2256,8 @@ def TypeNullUnspecified : TypeAttr { def CountedBy : DeclOrTypeAttr { let Spellings = [Clang<"counted_by">]; let Subjects = SubjectList<[Field], ErrorDiag>; - let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel">]; + let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; + let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; let LangOpts = [COnly]; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 4cb4f3d999f7a..4fad4d1a0eca7 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1447,6 +1447,10 @@ def FunctionMultiVersioning def NoDeref : DiagGroup<"noderef">; +// -fbounds-safety and bounds annotation related warnings +def BoundsSafetyCountedByEltTyUnknownSize : + DiagGroup<"bounds-safety-counted-by-elt-type-unknown-size">; + // A group for cross translation unit static analysis related warnings. def CrossTU : DiagGroup<"ctu">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c7dea1d54d063..0e73e0207766e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6544,8 +6544,10 @@ def warn_superclass_variable_sized_type_not_at_end : Warning< def err_flexible_array_count_not_in_same_struct : Error< "'counted_by' field %0 isn't within the same struct as the flexible array">; -def err_counted_by_attr_not_on_flexible_array_member : Error< - "'counted_by' only applies to C99 flexible array members">; +def err_counted_by_attr_not_on_ptr_or_flexible_array_member : Error< + "'counted_by' only applies to pointers or C99 flexible array members">; +def err_counted_by_attr_on_array_not_flexible_array_member : Error< + "'counted_by' on arrays only applies to C99 flexible array members">; def err_counted_by_attr_refer_to_itself : Error< "'counted_by' cannot refer to the flexible array member %0">; def err_counted_by_must_be_in_structure : Error< @@ -6560,6 +6562,23 @@ def err_counted_by_attr_refer_to_union : Error< "'counted_by' argument cannot refer to a union member">; def note_flexible_array_counted_by_attr_field : Note< "field %0 declared here">; +def err_counted_by_attr_pointee_unknown_size : Error< + "'counted_by' %select{cannot|should not}3 be applied to %select{" + "a pointer with pointee|" // pointer + "an array with element}0" // array + " of unknown size because %1 is %select{" + "an incomplete type|" // CountedByInvalidPointeeTypeKind::INCOMPLETE + "a sizeless type|" // CountedByInvalidPointeeTypeKind::SIZELESS + "a function type|" // CountedByInvalidPointeeTypeKind::FUNCTION + // CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER + "a struct type with a flexible array member" + "%select{|. This will be an error in a future compiler version}3" + "" + "}2">; + +def warn_counted_by_attr_elt_type_unknown_size : + Warning, + InGroup; let CategoryName = "ARC Semantic Issue" in { diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 3c4ab649e3b4c..0e6b2b0028f34 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1646,8 +1646,12 @@ class Parser : public CodeCompletionHandler { void ParseLexedAttributes(ParsingClass &Class); void ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D, bool EnterScope, bool OnDefinition); + void ParseLexedCAttributeList(LateParsedAttrList &LA, bool EnterScope, + ParsedAttributes *OutAttrs = nullptr); void ParseLexedAttribute(LateParsedAttribute &LA, bool EnterScope, bool OnDefinition); + void ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope, + ParsedAttributes *OutAttrs = nullptr); void ParseLexedMethodDeclarations(ParsingClass &Class); void ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM); void ParseLexedMethodDefs(ParsingClass &Class); @@ -2534,7 +2538,8 @@ class Parser : public CodeCompletionHandler { void ParseStructDeclaration( ParsingDeclSpec &DS, - llvm::function_ref FieldsCallback); + llvm::function_ref FieldsCallback, + LateParsedAttrList *LateFieldAttrs = nullptr); DeclGroupPtrTy ParseTopLevelStmtDecl(); @@ -3112,6 +3117,8 @@ class Parser : public CodeCompletionHandler { SourceLocation ScopeLoc, ParsedAttr::Form Form); + void DistributeCLateParsedAttrs(Decl *Dcl, LateParsedAttrList *LateAttrs); + void ParseBoundsAttribute(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 01ddba5eaf01d..a37e22055d852 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11381,7 +11381,8 @@ class Sema final : public SemaBase { QualType BuildMatrixType(QualType T, Expr *NumRows, Expr *NumColumns, SourceLocation AttrLoc); - QualType BuildCountAttributedArrayType(QualType WrappedTy, Expr *CountExpr); + QualType BuildCountAttributedArrayOrPointerType(QualType WrappedTy, + Expr *CountExpr); QualType BuildAddressSpaceAttr(QualType &T, LangAS ASIdx, Expr *AddrSpace, SourceLocation AttrLoc); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 3b90b8229dd18..04f105c128872 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -632,6 +632,16 @@ bool Type::isStructureType() const { return false; } +bool Type::isStructureTypeWithFlexibleArrayMember() const { + const auto *RT = getAs(); + if (!RT) + return false; + const auto *Decl = RT->getDecl(); + if (!Decl->isStruct()) + return false; + return Decl->hasFlexibleArrayMember(); +} + bool Type::isObjCBoxableRecordType() const { if (const auto *RT = getAs()) return RT->getDecl()->hasAttr(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 445d3fd66e387..c9819088d0f50 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3306,6 +3306,19 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, } } +void Parser::DistributeCLateParsedAttrs(Decl *Dcl, + LateParsedAttrList *LateAttrs) { + if (!LateAttrs) + return; + + if (Dcl) { + for (auto *LateAttr : *LateAttrs) { + if (LateAttr->Decls.empty()) + LateAttr->addDecl(Dcl); + } + } +} + /// Bounds attributes (e.g., counted_by): /// AttrName '(' expression ')' void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName, @@ -4843,13 +4856,14 @@ static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS, /// void Parser::ParseStructDeclaration( ParsingDeclSpec &DS, - llvm::function_ref FieldsCallback) { + llvm::function_ref FieldsCallback, + LateParsedAttrList *LateFieldAttrs) { if (Tok.is(tok::kw___extension__)) { // __extension__ silences extension warnings in the subexpression. ExtensionRAIIObject O(Diags); // Use RAII to do this. ConsumeToken(); - return ParseStructDeclaration(DS, FieldsCallback); + return ParseStructDeclaration(DS, FieldsCallback, LateFieldAttrs); } // Parse leading attributes. @@ -4914,10 +4928,12 @@ void Parser::ParseStructDeclaration( } // If attributes exist after the declarator, parse them. - MaybeParseGNUAttributes(DeclaratorInfo.D); + MaybeParseGNUAttributes(DeclaratorInfo.D, LateFieldAttrs); // We're done with this declarator; invoke the callback. - FieldsCallback(DeclaratorInfo); + Decl *Field = FieldsCallback(DeclaratorInfo); + if (Field) + DistributeCLateParsedAttrs(Field, LateFieldAttrs); // If we don't have a comma, it is either the end of the list (a ';') // or an error, bail out. @@ -4928,6 +4944,73 @@ void Parser::ParseStructDeclaration( } } +// TODO: All callers of this function should be moved to +// `Parser::ParseLexedAttributeList`. +void Parser::ParseLexedCAttributeList(LateParsedAttrList &LAs, bool EnterScope, + ParsedAttributes *OutAttrs) { + assert(LAs.parseSoon() && + "Attribute list should be marked for immediate parsing."); + for (auto *LA : LAs) { + ParseLexedCAttribute(*LA, EnterScope, OutAttrs); + delete LA; + } + LAs.clear(); +} + +/// Finish parsing an attribute for which parsing was delayed. +/// This will be called at the end of parsing a class declaration +/// for each LateParsedAttribute. We consume the saved tokens and +/// create an attribute with the arguments filled in. We add this +/// to the Attribute list for the decl. +void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope, + ParsedAttributes *OutAttrs) { + // Create a fake EOF so that attribute parsing won't go off the end of the + // attribute. + Token AttrEnd; + AttrEnd.startToken(); + AttrEnd.setKind(tok::eof); + AttrEnd.setLocation(Tok.getLocation()); + AttrEnd.setEofData(LA.Toks.data()); + LA.Toks.push_back(AttrEnd); + + // Append the current token at the end of the new token stream so that it + // doesn't get lost. + LA.Toks.push_back(Tok); + PP.EnterTokenStream(LA.Toks, /*DisableMacroExpansion=*/true, + /*IsReinject=*/true); + // Drop the current token and bring the first cached one. It's the same token + // as when we entered this function. + ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + + // TODO: Use `EnterScope` + (void)EnterScope; + + ParsedAttributes Attrs(AttrFactory); + + assert(LA.Decls.size() <= 1 && + "late field attribute expects to have at most one declaration."); + + // Dispatch based on the attribute and parse it + ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr, nullptr, + SourceLocation(), ParsedAttr::Form::GNU(), nullptr); + + for (auto *D : LA.Decls) + Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs); + + // Due to a parsing error, we either went over the cached tokens or + // there are still cached tokens left, so we skip the leftover tokens. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + // Consume the fake EOF token if it's there + if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData()) + ConsumeAnyToken(); + + if (OutAttrs) { + OutAttrs->takeAllFrom(Attrs); + } +} + /// ParseStructUnionBody /// struct-contents: /// struct-declaration-list @@ -4951,6 +5034,11 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope); Actions.ActOnTagStartDefinition(getCurScope(), TagDecl); + // `LateAttrParseExperimentalExtOnly=true` requests that only attributes + // marked with `LateAttrParseExperimentalExt` are late parsed. + LateParsedAttrList LateFieldAttrs(/*PSoon=*/true, + /*LateAttrParseExperimentalExtOnly=*/true); + // While we still have something to read, read the declarations in the struct. while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { @@ -5001,18 +5089,19 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, } if (!Tok.is(tok::at)) { - auto CFieldCallback = [&](ParsingFieldDeclarator &FD) { + auto CFieldCallback = [&](ParsingFieldDeclarator &FD) -> Decl * { // Install the declarator into the current TagDecl. Decl *Field = Actions.ActOnField(getCurScope(), TagDecl, FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D, FD.BitfieldSize); FD.complete(Field); + return Field; }; // Parse all the comma separated declarators. ParsingDeclSpec DS(*this); - ParseStructDeclaration(DS, CFieldCallback); + ParseStructDeclaration(DS, CFieldCallback, &LateFieldAttrs); } else { // Handle @defs ConsumeToken(); if (!Tok.isObjCAtKeyword(tok::objc_defs)) { @@ -5053,7 +5142,10 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, ParsedAttributes attrs(AttrFactory); // If attributes exist after struct contents, parse them. - MaybeParseGNUAttributes(attrs); + MaybeParseGNUAttributes(attrs, &LateFieldAttrs); + + // Late parse field attributes if necessary. + ParseLexedCAttributeList(LateFieldAttrs, /*EnterScope=*/false); SmallVector FieldDecls(TagDecl->fields()); diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 89f4acbd25e49..6a2088a73c55b 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -780,16 +780,16 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, } bool addedToDeclSpec = false; - auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) { + auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) -> Decl * { if (FD.D.getIdentifier() == nullptr) { Diag(AtLoc, diag::err_objc_property_requires_field_name) << FD.D.getSourceRange(); - return; + return nullptr; } if (FD.BitfieldSize) { Diag(AtLoc, diag::err_objc_property_bitfield) << FD.D.getSourceRange(); - return; + return nullptr; } // Map a nullability property attribute to a context-sensitive keyword @@ -818,6 +818,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, MethodImplKind); FD.complete(Property); + return Property; }; // Parse all the comma separated declarators. @@ -2013,7 +2014,7 @@ void Parser::ParseObjCClassInstanceVariables(ObjCContainerDecl *interfaceDecl, continue; } - auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) { + auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) -> Decl * { assert(getObjCDeclContext() == interfaceDecl && "Ivar should have interfaceDecl as its decl context"); // Install the declarator into the interface decl. @@ -2024,6 +2025,7 @@ void Parser::ParseObjCClassInstanceVariables(ObjCContainerDecl *interfaceDecl, if (Field) AllIvarDecls.push_back(Field); FD.complete(Field); + return Field; }; // Parse all the comma separated declarators. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index ca5938083917f..5041fd65286fa 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8663,31 +8663,95 @@ static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) { return RD; } -static bool -CheckCountExpr(Sema &S, FieldDecl *FD, Expr *E, - llvm::SmallVectorImpl &Decls) { +enum class CountedByInvalidPointeeTypeKind { + INCOMPLETE, + SIZELESS, + FUNCTION, + FLEXIBLE_ARRAY_MEMBER, + VALID, +}; + +static bool CheckCountedByAttrOnField( + Sema &S, FieldDecl *FD, Expr *E, + llvm::SmallVectorImpl &Decls) { + // Check the context the attribute is used in + if (FD->getParent()->isUnion()) { S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union) << FD->getSourceRange(); return true; } - if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) { - S.Diag(E->getBeginLoc(), diag::err_counted_by_attr_argument_not_integer) - << E->getSourceRange(); + const auto FieldTy = FD->getType(); + if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) { + S.Diag(FD->getBeginLoc(), + diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member) + << FD->getLocation(); return true; } LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = LangOptions::StrictFlexArraysLevelKind::IncompleteOnly; - - if (!Decl::isFlexibleArrayMemberLike(S.getASTContext(), FD, FD->getType(), + if (FieldTy->isArrayType() && + !Decl::isFlexibleArrayMemberLike(S.getASTContext(), FD, FieldTy, StrictFlexArraysLevel, true)) { - // The "counted_by" attribute must be on a flexible array member. - SourceRange SR = FD->getLocation(); - S.Diag(SR.getBegin(), - diag::err_counted_by_attr_not_on_flexible_array_member) - << SR; + S.Diag(FD->getBeginLoc(), + diag::err_counted_by_attr_on_array_not_flexible_array_member) + << FD->getLocation(); + 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 = S.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 (PointeeTy->isIncompleteType()) { + InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE; + } else if (PointeeTy->isSizelessType()) { + InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS; + } else if (PointeeTy->isFunctionType()) { + InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION; + } else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) { + if (FieldTy->isArrayType()) { + // 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; + S.Diag(FD->getBeginLoc(), DiagID) + << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind + << (ShouldWarn ? 1 : 0) << FD->getSourceRange(); + return true; + } + + // Check the expression + + if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) { + S.Diag(E->getBeginLoc(), diag::err_counted_by_attr_argument_not_integer) + << E->getSourceRange(); return true; } @@ -8750,10 +8814,11 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { return; llvm::SmallVector Decls; - if (CheckCountExpr(S, FD, CountExpr, Decls)) + if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls)) return; - QualType CAT = S.BuildCountAttributedArrayType(FD->getType(), CountExpr); + QualType CAT = + S.BuildCountAttributedArrayOrPointerType(FD->getType(), CountExpr); FD->setType(CAT); } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index c19c8cc34dd3b..ef0b6b701a52c 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -9345,9 +9345,9 @@ BuildTypeCoupledDecls(Expr *E, Decls.push_back(TypeCoupledDeclRefInfo(CountDecl, /*IsDref*/ false)); } -QualType Sema::BuildCountAttributedArrayType(QualType WrappedTy, - Expr *CountExpr) { - assert(WrappedTy->isIncompleteArrayType()); +QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy, + Expr *CountExpr) { + assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType()); llvm::SmallVector Decls; BuildTypeCoupledDecls(CountExpr, Decls); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 6b53c2490cc49..0ba402c417ceb 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7337,7 +7337,7 @@ QualType TreeTransform::TransformCountAttributedType( if (getDerived().AlwaysRebuild() || InnerTy != OldTy->desugar() || OldCount != NewCount) { // Currently, CountAttributedType can only wrap incomplete array types. - Result = SemaRef.BuildCountAttributedArrayType(InnerTy, NewCount); + Result = SemaRef.BuildCountAttributedArrayOrPointerType(InnerTy, NewCount); } TLB.push(Result); diff --git a/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c b/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c new file mode 100644 index 0000000000000..a585a45eeff03 --- /dev/null +++ b/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes %s -ast-dump | FileCheck %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_known { + int field; +}; + +//============================================================================== +// __counted_by on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + struct size_known *buf __counted_by(count); + int count; +}; +// CHECK-LABEL: struct on_member_pointer_complete_ty definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by(count)':'struct size_known *' +// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int' + +struct on_pointer_anon_count { + struct size_known *buf __counted_by(count); + struct { + int count; + }; +}; + +// CHECK-LABEL: struct on_pointer_anon_count definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by(count)':'struct size_known *' +// CHECK-NEXT: |-RecordDecl {{.*}} struct definition +// CHECK-NEXT: | `-FieldDecl {{.*}} count 'int' +// CHECK-NEXT: |-FieldDecl {{.*}} implicit 'struct on_pointer_anon_count::(anonymous at {{.*}})' +// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit referenced count 'int' +// CHECK-NEXT: |-Field {{.*}} '' 'struct on_pointer_anon_count::(anonymous at {{.*}})' +// CHECK-NEXT: `-Field {{.*}} 'count' 'int' + +//============================================================================== +// __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. +// +// See `clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c` for test +// cases. diff --git a/clang/test/AST/attr-counted-by-struct-ptrs.c b/clang/test/AST/attr-counted-by-struct-ptrs.c new file mode 100644 index 0000000000000..79a453d239cd5 --- /dev/null +++ b/clang/test/AST/attr-counted-by-struct-ptrs.c @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 %s -ast-dump | FileCheck %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +//============================================================================== +// __counted_by on struct member pointer in decl attribute position +//============================================================================== + +// CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_complete_ty definition +// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known * __counted_by(count)':'struct size_known *' +struct on_member_pointer_complete_ty { + int count; + struct size_known * buf __counted_by(count); +}; + +// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_buf definition +// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: |-RecordDecl {{.+}} struct definition +// CHECK-NEXT: | `-FieldDecl {{.+}} buf 'struct size_known * __counted_by(count)':'struct size_known *' +// CHECK-NEXT: |-FieldDecl {{.+}} implicit 'struct on_pointer_anon_buf::(anonymous at [[ANON_STRUCT_PATH:.+]])' +// CHECK-NEXT: `-IndirectFieldDecl {{.+}} implicit buf 'struct size_known * __counted_by(count)':'struct size_known *' +// CHECK-NEXT: |-Field {{.+}} '' 'struct on_pointer_anon_buf::(anonymous at [[ANON_STRUCT_PATH]])' +// CHECK-NEXT: `-Field {{.+}} 'buf' 'struct size_known * __counted_by(count)':'struct size_known *' +struct on_pointer_anon_buf { + int count; + struct { + struct size_known *buf __counted_by(count); + }; +}; + +struct on_pointer_anon_count { + struct { + int count; + }; + struct size_known *buf __counted_by(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' +// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known * __counted_by(count)':'struct size_known *' +struct on_member_pointer_complete_ty_ty_pos { + int count; + 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 **' +struct on_nested_pointer_outer { + int count; + struct size_known **__counted_by(count) buf; +}; + +// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_buf_ty_pos definition +// CHECK-NEXT: |-FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: |-RecordDecl {{.+}} struct definition +// CHECK-NEXT: | `-FieldDecl {{.+}} buf 'struct size_known * __counted_by(count)':'struct size_known *' +// CHECK-NEXT: |-FieldDecl {{.+}} implicit 'struct on_pointer_anon_buf_ty_pos::(anonymous at [[ANON_STRUCT_PATH2:.+]])' +// CHECK-NEXT: `-IndirectFieldDecl {{.+}} implicit buf 'struct size_known * __counted_by(count)':'struct size_known *' +// CHECK-NEXT: |-Field {{.+}} '' 'struct on_pointer_anon_buf_ty_pos::(anonymous at [[ANON_STRUCT_PATH2]])' +// CHECK-NEXT: `-Field {{.+}} 'buf' 'struct size_known * __counted_by(count)':'struct size_known *' +struct on_pointer_anon_buf_ty_pos { + int count; + struct { + struct size_known * __counted_by(count) buf; + }; +}; + +// CHECK-LABEL: RecordDecl {{.+}} struct on_pointer_anon_count_ty_pos definition +// CHECK-NEXT: |-RecordDecl {{.+}} struct definition +// CHECK-NEXT: | `-FieldDecl {{.+}} count 'int' +// CHECK-NEXT: |-FieldDecl {{.+}} implicit 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3:.+]])' +// CHECK-NEXT: |-IndirectFieldDecl {{.+}} implicit referenced count 'int' +// CHECK-NEXT: | |-Field {{.+}} '' 'struct on_pointer_anon_count_ty_pos::(anonymous at [[ANON_STRUCT_PATH3]])' +// CHECK-NEXT: | `-Field {{.+}} 'count' 'int' +struct on_pointer_anon_count_ty_pos { + struct { + int count; + }; + struct size_known *__counted_by(count) buf; +}; diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index fd0e6d71baa80..99732694f72a5 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -63,7 +63,6 @@ // CHECK-NEXT: CoroOnlyDestroyWhenComplete (SubjectMatchRule_record) // CHECK-NEXT: CoroReturnType (SubjectMatchRule_record) // CHECK-NEXT: CoroWrapper (SubjectMatchRule_function) -// CHECK-NEXT: CountedBy (SubjectMatchRule_field) // CHECK-NEXT: DLLExport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface) // CHECK-NEXT: DLLImport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface) // CHECK-NEXT: Destructor (SubjectMatchRule_function) diff --git a/clang/test/Sema/attr-counted-by-late-parsed-off.c b/clang/test/Sema/attr-counted-by-late-parsed-off.c new file mode 100644 index 0000000000000..34f51d10c0838 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-late-parsed-off.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -DNEEDS_LATE_PARSING -fno-experimental-late-parse-attributes -fsyntax-only -verify %s +// RUN: %clang_cc1 -DNEEDS_LATE_PARSING -fsyntax-only -verify %s + +// RUN: %clang_cc1 -UNEEDS_LATE_PARSING -fno-experimental-late-parse-attributes -fsyntax-only -verify=ok %s +// RUN: %clang_cc1 -UNEEDS_LATE_PARSING -fsyntax-only -verify=ok %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_known { int dummy; }; + +#ifdef NEEDS_LATE_PARSING +struct on_decl { + // expected-error@+1{{use of undeclared identifier 'count'}} + struct size_known *buf __counted_by(count); + int count; +}; + +#else + +// ok-no-diagnostics +struct on_decl { + int count; + struct size_known *buf __counted_by(count); +}; + +#endif 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 new file mode 100644 index 0000000000000..9ff3b080f6576 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c @@ -0,0 +1,254 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +typedef void(*fn_ptr_ty)(void); + +//============================================================================== +// __counted_by on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + struct size_known * buf __counted_by(count); + int count; +}; + +struct on_member_pointer_incomplete_ty { + struct size_unknown * buf __counted_by(count); // expected-error{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} + int count; +}; + +struct on_member_pointer_const_incomplete_ty { + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}} + const struct size_unknown * buf __counted_by(count); + int count; +}; + +struct on_member_pointer_void_ty { + void* buf __counted_by(count); // expected-error{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}} + int count; +}; + +struct on_member_pointer_fn_ptr_ty { + // buffer of `count` function pointers is allowed + void (**fn_ptr)(void) __counted_by(count); + int count; +}; + + +struct on_member_pointer_fn_ptr_ty_ptr_ty { + // buffer of `count` function pointers is allowed + fn_ptr_ty* fn_ptr __counted_by(count); + int count; +}; + +struct on_member_pointer_fn_ty { + // buffer of `count` functions is not allowed + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} + void (*fn_ptr)(void) __counted_by(count); + int count; +}; + +struct on_member_pointer_fn_ptr_ty_ty { + // buffer of `count` functions is not allowed + // 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 fn_ptr __counted_by(count); + int count; +}; + +struct has_unannotated_vla { + int count; + int buffer[]; +}; + +struct on_member_pointer_struct_with_vla { + // 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* objects __counted_by(count); + int count; +}; + +struct has_annotated_vla { + int count; + int buffer[] __counted_by(count); +}; + +// Currently prevented because computing the size of `objects` at runtime would +// require an O(N) walk of `objects` to take into account the length of the VLA +// in each struct instance. +struct on_member_pointer_struct_with_annotated_vla { + // 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* objects __counted_by(count); + int count; +}; + +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; +}; + +struct on_pointer_anon_count { + struct size_known *buf __counted_by(count); + struct { + int 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; + 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'}} + 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'}} + 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'}} + 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'}} + 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'}} + 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'}} + 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_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; + }; +}; + +//============================================================================== +// __counted_by on struct non-pointer members +//============================================================================== + +struct on_pod_ty { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int wrong_ty __counted_by(count); + int count; +}; + +struct on_void_ty { + // expected-error@+2{{'counted_by' only applies to pointers or C99 flexible array members}} + // expected-error@+1{{field has incomplete type 'void'}} + void wrong_ty __counted_by(count); + int count; +}; diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs-sizeless-types.c b/clang/test/Sema/attr-counted-by-struct-ptrs-sizeless-types.c new file mode 100644 index 0000000000000..9b0f2eafb13c2 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-struct-ptrs-sizeless-types.c @@ -0,0 +1,17 @@ +// __SVInt8_t is specific to ARM64 so specify that in the target triple +// RUN: %clang_cc1 -triple arm64-apple-darwin -fsyntax-only -verify %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct on_sizeless_pointee_ty { + int count; + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because '__SVInt8_t' is a sizeless type}} + __SVInt8_t* member __counted_by(count); +}; + +struct on_sizeless_ty { + int count; + // expected-error@+2{{'counted_by' only applies to pointers or C99 flexible array members}} + // expected-error@+1{{field has sizeless type '__SVInt8_t'}} + __SVInt8_t member __counted_by(count); +}; diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs.c b/clang/test/Sema/attr-counted-by-struct-ptrs.c new file mode 100644 index 0000000000000..cd2bfe36938b2 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-struct-ptrs.c @@ -0,0 +1,224 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +typedef void(*fn_ptr_ty)(void); + +//============================================================================== +// __counted_by on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + int count; + struct size_known * buf __counted_by(count); +}; + +struct on_member_pointer_incomplete_ty { + int count; + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} + struct size_unknown * buf __counted_by(count); +}; + +struct on_member_pointer_const_incomplete_ty { + int count; + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}} + const struct size_unknown * buf __counted_by(count); +}; + +struct on_member_pointer_void_ty { + int count; + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}} + void* buf __counted_by(count); +}; + +struct on_member_pointer_fn_ptr_ty { + int count; + // buffer of `count` function pointers is allowed + void (**fn_ptr)(void) __counted_by(count); +}; + +struct on_member_pointer_fn_ptr_ty_ptr_ty { + int count; + // buffer of `count` function pointers is allowed + fn_ptr_ty* fn_ptr __counted_by(count); +}; + +struct on_member_pointer_fn_ty { + int count; + // buffer of `count` functions is not allowed + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} + void (*fn_ptr)(void) __counted_by(count); +}; + +struct on_member_pointer_fn_ptr_ty_ty { + int count; + // buffer of `count` functions is not allowed + // 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 fn_ptr __counted_by(count); +}; + +struct has_unannotated_vla { + int count; + int buffer[]; +}; + +struct on_member_pointer_struct_with_vla { + int 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* objects __counted_by(count); +}; + +struct has_annotated_vla { + int count; + int buffer[] __counted_by(count); +}; + +// Currently prevented because computing the size of `objects` at runtime would +// require an O(N) walk of `objects` to take into account the length of the VLA +// in each struct instance. +struct on_member_pointer_struct_with_annotated_vla { + int 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* objects __counted_by(count); +}; + +struct on_pointer_anon_buf { + int count; + struct { + struct size_known *buf __counted_by(count); + }; +}; + +struct on_pointer_anon_count { + struct { + int count; + }; + struct size_known *buf __counted_by(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; + struct size_known *__counted_by(count) buf; +}; + +struct on_member_pointer_incomplete_ty_ty_pos { + int count; + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} + struct size_unknown * __counted_by(count) buf; +}; + +struct on_member_pointer_const_incomplete_ty_ty_pos { + int count; + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'const struct size_unknown' is an incomplete type}} + const struct size_unknown * __counted_by(count) buf; +}; + +struct on_member_pointer_void_ty_ty_pos { + int count; + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}} + void *__counted_by(count) buf; +}; + +// - + +struct on_member_pointer_fn_ptr_ty_pos { + int count; + // buffer of `count` function pointers is allowed + void (** __counted_by(count) fn_ptr)(void); +}; + +struct on_member_pointer_fn_ptr_ty_ptr_ty_pos { + int count; + // buffer of `count` function pointers is allowed + fn_ptr_ty* __counted_by(count) fn_ptr; +}; + +struct on_member_pointer_fn_ty_ty_pos { + int count; + // buffer of `count` functions is not allowed + // 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); +}; + +struct on_member_pointer_fn_ptr_ty_ty_pos { + int count; + // buffer of `count` functions is not allowed + // 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; +}; + +// 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); +}; + +struct on_member_pointer_struct_with_vla_ty_pos { + int 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; +}; + +// Currently prevented because computing the size of `objects` at runtime would +// require an O(N) walk of `objects` to take into account the length of the VLA +// in each struct instance. +struct on_member_pointer_struct_with_annotated_vla_ty_pos { + int 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; +}; + +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; +}; + +struct on_nested_pointer_outer { + int count; + struct size_known **__counted_by(count) buf; +}; + +struct on_pointer_anon_buf_ty_pos { + int count; + struct { + struct size_known * __counted_by(count) buf; + }; +}; + +struct on_pointer_anon_count_ty_pos { + struct { + int count; + }; + struct size_known *__counted_by(count) buf; +}; + +//============================================================================== +// __counted_by on struct non-pointer members +//============================================================================== + +struct on_pod_ty { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int wrong_ty __counted_by(count); +}; + +struct on_void_ty { + int count; + // expected-error@+2{{'counted_by' only applies to pointers or C99 flexible array members}} + // expected-error@+1{{field has incomplete type 'void'}} + void wrong_ty __counted_by(count); +}; diff --git a/clang/test/Sema/attr-counted-by-vla-sizeless-types.c b/clang/test/Sema/attr-counted-by-vla-sizeless-types.c new file mode 100644 index 0000000000000..31c0007501c48 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-vla-sizeless-types.c @@ -0,0 +1,11 @@ +// __SVInt8_t is specific to ARM64 so specify that in the target triple +// RUN: %clang_cc1 -triple arm64-apple-darwin -fsyntax-only -verify %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct on_sizeless_elt_ty { + int count; + // expected-error@+2{{'counted_by' only applies to pointers or C99 flexible array members}} + // expected-error@+1{{array has sizeless element type '__SVInt8_t'}} + __SVInt8_t arr[] __counted_by(count); +}; diff --git a/clang/test/Sema/attr-counted-by-vla.c b/clang/test/Sema/attr-counted-by-vla.c new file mode 100644 index 0000000000000..b25f719f3b95a --- /dev/null +++ b/clang/test/Sema/attr-counted-by-vla.c @@ -0,0 +1,196 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct bar; + +struct not_found { + int count; + struct bar *fam[] __counted_by(bork); // expected-error {{use of undeclared identifier 'bork'}} +}; + +struct no_found_count_not_in_substruct { + unsigned long flags; + unsigned char count; // expected-note {{'count' declared here}} + struct A { + int dummy; + int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}} + } a; +}; + +struct not_found_count_not_in_unnamed_substruct { + unsigned char count; // expected-note {{'count' declared here}} + struct { + int dummy; + int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}} + } a; +}; + +struct not_found_count_not_in_unnamed_substruct_2 { + struct { + unsigned char count; // expected-note {{'count' declared here}} + }; + struct { + int dummy; + int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}} + } a; +}; + +struct not_found_count_in_other_unnamed_substruct { + struct { + unsigned char count; + } a1; + + struct { + int dummy; + int array[] __counted_by(count); // expected-error {{use of undeclared identifier 'count'}} + }; +}; + +struct not_found_count_in_other_substruct { + struct _a1 { + unsigned char count; + } a1; + + struct { + int dummy; + int array[] __counted_by(count); // expected-error {{use of undeclared identifier 'count'}} + }; +}; + +struct not_found_count_in_other_substruct_2 { + struct _a2 { + unsigned char count; + } a2; + + int array[] __counted_by(count); // expected-error {{use of undeclared identifier 'count'}} +}; + +struct not_found_suggest { + int bork; + struct bar *fam[] __counted_by(blork); // expected-error {{use of undeclared identifier 'blork'}} +}; + +int global; // expected-note {{'global' declared here}} + +struct found_outside_of_struct { + int bork; + struct bar *fam[] __counted_by(global); // expected-error {{field 'global' in 'counted_by' not inside structure}} +}; + +struct self_referrential { + int bork; + struct bar *self[] __counted_by(self); // expected-error {{use of undeclared identifier 'self'}} +}; + +struct non_int_count { + double dbl_count; + struct bar *fam[] __counted_by(dbl_count); // expected-error {{'counted_by' requires a non-boolean integer type argument}} +}; + +struct array_of_ints_count { + int integers[2]; + struct bar *fam[] __counted_by(integers); // expected-error {{'counted_by' requires a non-boolean integer type argument}} +}; + +struct not_a_fam { + int count; + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'struct bar' is an incomplete type}} + struct bar *non_fam __counted_by(count); +}; + +struct not_a_c99_fam { + int count; + struct bar *non_c99_fam[0] __counted_by(count); // expected-error {{'counted_by' on arrays only applies to C99 flexible array members}} +}; + +struct annotated_with_anon_struct { + unsigned long flags; + struct { + unsigned char count; + int array[] __counted_by(crount); // expected-error {{use of undeclared identifier 'crount'}} + }; +}; + +//============================================================================== +// __counted_by on a struct VLA with element type that has unknown size +//============================================================================== + +struct size_unknown; // expected-note 2{{forward declaration of 'struct size_unknown'}} +struct on_member_arr_incomplete_ty_ty_pos { + int count; + // expected-error@+2{{'counted_by' only applies to pointers or C99 flexible array members}} + // expected-error@+1{{array has incomplete element type 'struct size_unknown'}} + struct size_unknown buf[] __counted_by(count); +}; + +struct on_member_arr_incomplete_const_ty_ty_pos { + int count; + // expected-error@+2{{'counted_by' only applies to pointers or C99 flexible array members}} + // expected-error@+1{{array has incomplete element type 'const struct size_unknown'}} + const struct size_unknown buf[] __counted_by(count); +}; + +struct on_member_arr_void_ty_ty_pos { + int count; + // expected-error@+2{{'counted_by' only applies to pointers or C99 flexible array members}} + // expected-error@+1{{array has incomplete element type 'void'}} + void buf[] __counted_by(count); +}; + +typedef void(fn_ty)(int); + +struct on_member_arr_fn_ptr_ty { + int count; + // An Array of function pointers is allowed + fn_ty* buf[] __counted_by(count); +}; + +struct on_member_arr_fn_ty { + int count; + // An array of functions is not allowed. + // expected-error@+2{{'counted_by' only applies to pointers or C99 flexible array members}} + // expected-error@+1{{'buf' declared as array of functions of type 'fn_ty' (aka 'void (int)')}} + fn_ty buf[] __counted_by(count); +}; + + +// `buffer_of_structs_with_unnannotated_vla`, +// `buffer_of_structs_with_annotated_vla`, and +// `buffer_of_const_structs_with_annotated_vla` are currently prevented because +// computing the size of `Arr` at runtime would require an O(N) walk of `Arr` +// elements to take into account the length of the VLA in each struct instance. + +struct has_unannotated_VLA { + int count; + char buffer[]; +}; + +struct has_annotated_VLA { + int count; + char buffer[] __counted_by(count); +}; + +struct buffer_of_structs_with_unnannotated_vla { + int count; + // Treating this as a warning is a temporary fix for existing attribute adopters. It **SHOULD BE AN ERROR**. + // expected-warning@+1{{'counted_by' should not be applied to an array with element of unknown size because 'struct has_unannotated_VLA' is a struct type with a flexible array member. This will be an error in a future compiler version}} + struct has_unannotated_VLA Arr[] __counted_by(count); +}; + + +struct buffer_of_structs_with_annotated_vla { + int count; + // Treating this as a warning is a temporary fix for existing attribute adopters. It **SHOULD BE AN ERROR**. + // expected-warning@+1{{'counted_by' should not be applied to an array with element of unknown size because 'struct has_annotated_VLA' is a struct type with a flexible array member. This will be an error in a future compiler version}} + struct has_annotated_VLA Arr[] __counted_by(count); +}; + +struct buffer_of_const_structs_with_annotated_vla { + int count; + // Treating this as a warning is a temporary fix for existing attribute adopters. It **SHOULD BE AN ERROR**. + // Make sure the `const` qualifier is printed when printing the element type. + // expected-warning@+1{{'counted_by' should not be applied to an array with element of unknown size because 'const struct has_annotated_VLA' is a struct type with a flexible array member. This will be an error in a future compiler version}} + const struct has_annotated_VLA Arr[] __counted_by(count); +}; + diff --git a/clang/test/Sema/attr-counted-by.c b/clang/test/Sema/attr-counted-by.c deleted file mode 100644 index d5d4ebf557392..0000000000000 --- a/clang/test/Sema/attr-counted-by.c +++ /dev/null @@ -1,112 +0,0 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s - -#define __counted_by(f) __attribute__((counted_by(f))) - -struct bar; - -struct not_found { - int count; - struct bar *fam[] __counted_by(bork); // expected-error {{use of undeclared identifier 'bork'}} -}; - -struct no_found_count_not_in_substruct { - unsigned long flags; - unsigned char count; // expected-note {{'count' declared here}} - struct A { - int dummy; - int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}} - } a; -}; - -struct not_found_count_not_in_unnamed_substruct { - unsigned char count; // expected-note {{'count' declared here}} - struct { - int dummy; - int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}} - } a; -}; - -struct not_found_count_not_in_unnamed_substruct_2 { - struct { - unsigned char count; // expected-note {{'count' declared here}} - }; - struct { - int dummy; - int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}} - } a; -}; - -struct not_found_count_in_other_unnamed_substruct { - struct { - unsigned char count; - } a1; - - struct { - int dummy; - int array[] __counted_by(count); // expected-error {{use of undeclared identifier 'count'}} - }; -}; - -struct not_found_count_in_other_substruct { - struct _a1 { - unsigned char count; - } a1; - - struct { - int dummy; - int array[] __counted_by(count); // expected-error {{use of undeclared identifier 'count'}} - }; -}; - -struct not_found_count_in_other_substruct_2 { - struct _a2 { - unsigned char count; - } a2; - - int array[] __counted_by(count); // expected-error {{use of undeclared identifier 'count'}} -}; - -struct not_found_suggest { - int bork; - struct bar *fam[] __counted_by(blork); // expected-error {{use of undeclared identifier 'blork'}} -}; - -int global; // expected-note {{'global' declared here}} - -struct found_outside_of_struct { - int bork; - struct bar *fam[] __counted_by(global); // expected-error {{field 'global' in 'counted_by' not inside structure}} -}; - -struct self_referrential { - int bork; - struct bar *self[] __counted_by(self); // expected-error {{use of undeclared identifier 'self'}} -}; - -struct non_int_count { - double dbl_count; - struct bar *fam[] __counted_by(dbl_count); // expected-error {{'counted_by' requires a non-boolean integer type argument}} -}; - -struct array_of_ints_count { - int integers[2]; - struct bar *fam[] __counted_by(integers); // expected-error {{'counted_by' requires a non-boolean integer type argument}} -}; - -struct not_a_fam { - int count; - struct bar *non_fam __counted_by(count); // expected-error {{'counted_by' only applies to C99 flexible array members}} -}; - -struct not_a_c99_fam { - int count; - struct bar *non_c99_fam[0] __counted_by(count); // expected-error {{'counted_by' only applies to C99 flexible array members}} -}; - -struct annotated_with_anon_struct { - unsigned long flags; - struct { - unsigned char count; - int array[] __counted_by(crount); // expected-error {{use of undeclared identifier 'crount'}} - }; -};