diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index d9f2b16155e29..5f5f3a34c0862 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -554,6 +554,15 @@ Attribute Changes in Clang size_t count; }; +- The attributes ``sized_by``, ``counted_by_or_null`` and ``sized_by_or_null``` + have been added as variants on ``counted_by``, each with slightly different semantics. + ``sized_by`` takes a byte size parameter instead of an element count, allowing pointees + with unknown size. The ``counted_by_or_null`` and ``sized_by_or_null`` variants are equivalent + to their base variants, except the pointer can be null regardless of count/size value. + If the pointer is null the size is effectively 0. ``sized_by_or_null`` is needed to properly + annotate allocator functions like ``malloc`` that return a buffer of a given byte size, but can + also return null. + - The ``guarded_by``, ``pt_guarded_by``, ``acquired_after``, ``acquired_before`` attributes now support referencing struct members in C. The arguments are also now late parsed when ``-fexperimental-late-parse-attributes`` is passed like diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 82befc656820e..d2d9dd24536cb 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2292,6 +2292,36 @@ def CountedBy : DeclOrTypeAttr { let LangOpts = [COnly]; } +def CountedByOrNull : DeclOrTypeAttr { + let Spellings = [Clang<"counted_by_or_null">]; + let Subjects = SubjectList<[Field], ErrorDiag>; + let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; + let LateParsed = LateAttrParseExperimentalExt; + let ParseArgumentsAsUnevaluated = 1; + let Documentation = [CountedByDocs]; + let LangOpts = [COnly]; +} + +def SizedBy : DeclOrTypeAttr { + let Spellings = [Clang<"sized_by">]; + let Subjects = SubjectList<[Field], ErrorDiag>; + let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>]; + let LateParsed = LateAttrParseExperimentalExt; + let ParseArgumentsAsUnevaluated = 1; + let Documentation = [CountedByDocs]; + let LangOpts = [COnly]; +} + +def SizedByOrNull : DeclOrTypeAttr { + let Spellings = [Clang<"sized_by_or_null">]; + let Subjects = SubjectList<[Field], ErrorDiag>; + let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>]; + let LateParsed = LateAttrParseExperimentalExt; + let ParseArgumentsAsUnevaluated = 1; + let Documentation = [CountedByDocs]; + let LangOpts = [COnly]; +} + // This is a marker used to indicate that an __unsafe_unretained qualifier was // ignored because ARC is not enabled. The usual representation for this // qualifier is as an ObjCOwnership attribute with Kind == "none". diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1aba8bc24ba2f..fa02622f12271 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6567,28 +6567,28 @@ def warn_superclass_variable_sized_type_not_at_end : Warning< "field %0 can overwrite instance variable %1 with variable sized type %2" " in superclass %3">, InGroup; -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_ptr_or_flexible_array_member : Error< - "'counted_by' only applies to pointers or C99 flexible array members">; +def err_count_attr_param_not_in_same_struct : Error< + "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' field %0 isn't within the same struct as the annotated %select{pointer|flexible array}2">; +def err_count_attr_not_on_ptr_or_flexible_array_member : Error< + "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' only applies to pointers%select{ or C99 flexible array members|||}0%select{|; did you mean to use 'counted_by'?}1">; 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< - "field %0 in 'counted_by' not inside structure">; -def err_counted_by_attr_argument_not_integer : Error< - "'counted_by' requires a non-boolean integer type argument">; -def err_counted_by_attr_only_support_simple_decl_reference : Error< - "'counted_by' argument must be a simple declaration reference">; -def err_counted_by_attr_in_union : Error< - "'counted_by' cannot be applied to a union member">; -def err_counted_by_attr_refer_to_union : Error< - "'counted_by' argument cannot refer to a union member">; +def err_count_attr_must_be_in_structure : Error< + "field %0 in '%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' not inside structure">; +def err_count_attr_argument_not_integer : Error< + "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' requires a non-boolean integer type argument">; +def err_count_attr_only_support_simple_decl_reference : Error< + "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' argument must be a simple declaration reference">; +def err_count_attr_in_union : Error< + "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' cannot be applied to a union member">; +def err_count_attr_refer_to_union : Error< + "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' 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{" + "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}4' %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{" diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4d6497755228e..57994f4033922 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -14603,7 +14603,9 @@ class Sema final : public SemaBase { SourceLocation AttrLoc); QualType BuildCountAttributedArrayOrPointerType(QualType WrappedTy, - Expr *CountExpr); + Expr *CountExpr, + bool CountInBytes, + bool OrNull); /// BuildAddressSpaceAttr - Builds a DependentAddressSpaceType if an /// expression is uninstantiated. If instantiated it will apply the diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 7c87fd587880e..ffec3ef9d2269 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1934,6 +1934,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, break; case attr::CountedBy: + case attr::CountedByOrNull: + case attr::SizedBy: + case attr::SizedByOrNull: case attr::LifetimeBound: case attr::TypeNonNull: case attr::TypeNullable: diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 0aae5f35edd20..7ce9a9cea1c7a 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -700,7 +700,10 @@ void Parser::ParseGNUAttributeArgs( ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc, Form); return; - } else if (AttrKind == ParsedAttr::AT_CountedBy) { + } else if (AttrKind == ParsedAttr::AT_CountedBy || + AttrKind == ParsedAttr::AT_CountedByOrNull || + AttrKind == ParsedAttr::AT_SizedBy || + AttrKind == ParsedAttr::AT_SizedByOrNull) { ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc, Form); return; @@ -4866,9 +4869,8 @@ static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS, for (const auto &DD : CAT->dependent_decls()) { if (!RD->containsDecl(DD.getDecl())) { - P.Diag(VD->getBeginLoc(), - diag::err_flexible_array_count_not_in_same_struct) - << DD.getDecl(); + P.Diag(VD->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct) + << DD.getDecl() << CAT->getKind() << CAT->isArrayType(); P.Diag(DD.getDecl()->getBeginLoc(), diag::note_flexible_array_counted_by_attr_field) << DD.getDecl(); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 2f16d0f76dbd9..73a85ff39667b 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5868,6 +5868,15 @@ static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) { return RD; } +static CountAttributedType::DynamicCountPointerKind +getCountAttrKind(bool CountInBytes, bool OrNull) { + if (CountInBytes) + return OrNull ? CountAttributedType::SizedByOrNull + : CountAttributedType::SizedBy; + return OrNull ? CountAttributedType::CountedByOrNull + : CountAttributedType::CountedBy; +} + enum class CountedByInvalidPointeeTypeKind { INCOMPLETE, SIZELESS, @@ -5876,22 +5885,31 @@ enum class CountedByInvalidPointeeTypeKind { VALID, }; -static bool CheckCountedByAttrOnField( - Sema &S, FieldDecl *FD, Expr *E, - llvm::SmallVectorImpl &Decls) { +static bool +CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E, + llvm::SmallVectorImpl &Decls, + bool CountInBytes, bool OrNull) { // Check the context the attribute is used in + unsigned Kind = getCountAttrKind(CountInBytes, OrNull); + if (FD->getParent()->isUnion()) { - S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union) - << FD->getSourceRange(); + S.Diag(FD->getBeginLoc(), diag::err_count_attr_in_union) + << Kind << FD->getSourceRange(); return true; } const auto FieldTy = FD->getType(); + if (FieldTy->isArrayType() && (CountInBytes || OrNull)) { + S.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()) { S.Diag(FD->getBeginLoc(), - diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member) - << FD->getLocation(); + diag::err_count_attr_not_on_ptr_or_flexible_array_member) + << Kind << FD->getLocation() << /* do not suggest counted_by */ 0; return true; } @@ -5902,7 +5920,7 @@ static bool CheckCountedByAttrOnField( StrictFlexArraysLevel, true)) { S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_on_array_not_flexible_array_member) - << FD->getLocation(); + << Kind << FD->getLocation(); return true; } @@ -5923,7 +5941,7 @@ static bool CheckCountedByAttrOnField( // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable // when `FieldTy->isArrayType()`. bool ShouldWarn = false; - if (PointeeTy->isIncompleteType()) { + if (PointeeTy->isIncompleteType() && !CountInBytes) { InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE; } else if (PointeeTy->isSizelessType()) { InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS; @@ -5948,23 +5966,23 @@ static bool CheckCountedByAttrOnField( : diag::err_counted_by_attr_pointee_unknown_size; S.Diag(FD->getBeginLoc(), DiagID) << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind - << (ShouldWarn ? 1 : 0) << FD->getSourceRange(); + << (ShouldWarn ? 1 : 0) << Kind << 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(); + S.Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer) + << Kind << E->getSourceRange(); return true; } auto *DRE = dyn_cast(E); if (!DRE) { S.Diag(E->getBeginLoc(), - diag::err_counted_by_attr_only_support_simple_decl_reference) - << E->getSourceRange(); + diag::err_count_attr_only_support_simple_decl_reference) + << Kind << E->getSourceRange(); return true; } @@ -5974,8 +5992,8 @@ static bool CheckCountedByAttrOnField( CountFD = IFD->getAnonField(); } if (!CountFD) { - S.Diag(E->getBeginLoc(), diag::err_counted_by_must_be_in_structure) - << CountDecl << E->getSourceRange(); + S.Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure) + << CountDecl << Kind << E->getSourceRange(); S.Diag(CountDecl->getBeginLoc(), diag::note_flexible_array_counted_by_attr_field) @@ -5985,8 +6003,8 @@ static bool CheckCountedByAttrOnField( if (FD->getParent() != CountFD->getParent()) { if (CountFD->getParent()->isUnion()) { - S.Diag(CountFD->getBeginLoc(), diag::err_counted_by_attr_refer_to_union) - << CountFD->getSourceRange(); + S.Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union) + << Kind << CountFD->getSourceRange(); return true; } // Whether CountRD is an anonymous struct is not determined at this @@ -5996,9 +6014,8 @@ static bool CheckCountedByAttrOnField( auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD); if (RD != CountRD) { - S.Diag(E->getBeginLoc(), - diag::err_flexible_array_count_not_in_same_struct) - << CountFD << E->getSourceRange(); + S.Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct) + << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange(); S.Diag(CountFD->getBeginLoc(), diag::note_flexible_array_counted_by_attr_field) << CountFD << CountFD->getSourceRange(); @@ -6018,12 +6035,35 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { 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"); + } + llvm::SmallVector Decls; - if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls)) + if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls, CountInBytes, OrNull)) return; - QualType CAT = - S.BuildCountAttributedArrayOrPointerType(FD->getType(), CountExpr); + QualType CAT = S.BuildCountAttributedArrayOrPointerType( + FD->getType(), CountExpr, CountInBytes, OrNull); FD->setType(CAT); } @@ -6971,6 +7011,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, break; case ParsedAttr::AT_CountedBy: + case ParsedAttr::AT_CountedByOrNull: + case ParsedAttr::AT_SizedBy: + case ParsedAttr::AT_SizedByOrNull: handleCountedByAttrField(S, D, AL); break; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index f3510a0091994..714409f927a8d 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -9324,15 +9324,17 @@ BuildTypeCoupledDecls(Expr *E, } QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy, - Expr *CountExpr) { + Expr *CountExpr, + bool CountInBytes, + bool OrNull) { assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType()); llvm::SmallVector Decls; BuildTypeCoupledDecls(CountExpr, Decls); /// When the resulting expression is invalid, we still create the AST using /// the original count expression for the sake of AST dump. - return Context.getCountAttributedType( - WrappedTy, CountExpr, /*CountInBytes*/ false, /*OrNull*/ false, Decls); + return Context.getCountAttributedType(WrappedTy, CountExpr, CountInBytes, + OrNull, Decls); } /// getDecltypeForExpr - Given an expr, will return the decltype for diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index d26c5a97fa3d2..655f248d11383 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7397,7 +7397,8 @@ QualType TreeTransform::TransformCountAttributedType( if (getDerived().AlwaysRebuild() || InnerTy != OldTy->desugar() || OldCount != NewCount) { // Currently, CountAttributedType can only wrap incomplete array types. - Result = SemaRef.BuildCountAttributedArrayOrPointerType(InnerTy, NewCount); + Result = SemaRef.BuildCountAttributedArrayOrPointerType( + InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull()); } TLB.push(Result); diff --git a/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c b/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c new file mode 100644 index 0000000000000..975c0a0231943 --- /dev/null +++ b/clang/test/AST/attr-counted-by-or-null-late-parsed-struct-ptrs.c @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes %s -ast-dump | FileCheck %s + +#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f))) + +struct size_known { + int field; +}; + +//============================================================================== +// __counted_by_or_null on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + struct size_known *buf __counted_by_or_null(count); + int count; +}; +// CHECK-LABEL: struct on_member_pointer_complete_ty definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by_or_null(count)':'struct size_known *' +// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int' + +struct on_pointer_anon_count { + struct size_known *buf __counted_by_or_null(count); + struct { + int count; + }; +}; + +// CHECK-LABEL: struct on_pointer_anon_count definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by_or_null(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_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. +// +// See `clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c` for test +// cases. 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 new file mode 100644 index 0000000000000..cedb3f1192eda --- /dev/null +++ b/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 %s -ast-dump | FileCheck %s + +#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +//============================================================================== +// __counted_by_or_null 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_or_null(count)':'struct size_known *' +struct on_member_pointer_complete_ty { + int count; + struct size_known * buf __counted_by_or_null(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_or_null(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_or_null(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_or_null(count)':'struct size_known *' +struct on_pointer_anon_buf { + int count; + struct { + struct size_known *buf __counted_by_or_null(count); + }; +}; + +struct on_pointer_anon_count { + struct { + int count; + }; + struct size_known *buf __counted_by_or_null(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 + +// 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_or_null(count)':'struct size_known *' +struct on_member_pointer_complete_ty_ty_pos { + int count; + 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' +// CHECK-NEXT: `-FieldDecl {{.+}} buf 'struct size_known ** __counted_by_or_null(count)':'struct size_known **' +struct on_nested_pointer_outer { + int count; + struct size_known **__counted_by_or_null(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_or_null(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_or_null(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_or_null(count)':'struct size_known *' +struct on_pointer_anon_buf_ty_pos { + int count; + struct { + struct size_known * __counted_by_or_null(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_or_null(count) buf; +}; diff --git a/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c b/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c new file mode 100644 index 0000000000000..b58caf608bf97 --- /dev/null +++ b/clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes %s -ast-dump | FileCheck %s + +#define __sized_by(f) __attribute__((sized_by(f))) + +struct size_known { + int field; +}; + +//============================================================================== +// __sized_by on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + struct size_known *buf __sized_by(count); + int count; +}; +// CHECK-LABEL: struct on_member_pointer_complete_ty definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __sized_by(count)':'struct size_known *' +// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int' + +struct on_pointer_anon_count { + struct size_known *buf __sized_by(count); + struct { + int count; + }; +}; + +// CHECK-LABEL: struct on_pointer_anon_count definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __sized_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' + +//============================================================================== +// __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 `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-sized-by-or-null-late-parsed-struct-ptrs.c b/clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c new file mode 100644 index 0000000000000..d55a42ac0fb94 --- /dev/null +++ b/clang/test/AST/attr-sized-by-or-null-late-parsed-struct-ptrs.c @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes %s -ast-dump | FileCheck %s + +#define __sized_by_or_null(f) __attribute__((sized_by_or_null(f))) + +struct size_known { + int field; +}; + +//============================================================================== +// __sized_by_or_null on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + struct size_known *buf __sized_by_or_null(count); + int count; +}; +// CHECK-LABEL: struct on_member_pointer_complete_ty definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __sized_by_or_null(count)':'struct size_known *' +// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int' + +struct on_pointer_anon_count { + struct size_known *buf __sized_by_or_null(count); + struct { + int count; + }; +}; + +// CHECK-LABEL: struct on_pointer_anon_count definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __sized_by_or_null(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' + +//============================================================================== +// __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 `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-sized-by-or-null-struct-ptrs.c b/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c new file mode 100644 index 0000000000000..6189799b85ccb --- /dev/null +++ b/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 %s -ast-dump | FileCheck %s + +#define __sized_by_or_null(f) __attribute__((sized_by_or_null(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +//============================================================================== +// __sized_by_or_null 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 * __sized_by_or_null(count)':'struct size_known *' +struct on_member_pointer_complete_ty { + int count; + struct size_known * buf __sized_by_or_null(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 * __sized_by_or_null(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 * __sized_by_or_null(count)':'struct size_known *' +// CHECK-NEXT: |-Field {{.+}} '' 'struct on_pointer_anon_buf::(anonymous at [[ANON_STRUCT_PATH]])' +// CHECK-NEXT: `-Field {{.+}} 'buf' 'struct size_known * __sized_by_or_null(count)':'struct size_known *' +struct on_pointer_anon_buf { + int count; + struct { + struct size_known *buf __sized_by_or_null(count); + }; +}; + +struct on_pointer_anon_count { + struct { + int count; + }; + struct size_known *buf __sized_by_or_null(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 + +// 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 * __sized_by_or_null(count)':'struct size_known *' +struct on_member_pointer_complete_ty_ty_pos { + int count; + 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 **' +struct on_nested_pointer_outer { + int count; + struct size_known **__sized_by_or_null(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 * __sized_by_or_null(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 * __sized_by_or_null(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 * __sized_by_or_null(count)':'struct size_known *' +struct on_pointer_anon_buf_ty_pos { + int count; + struct { + struct size_known * __sized_by_or_null(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 *__sized_by_or_null(count) buf; +}; diff --git a/clang/test/AST/attr-sized-by-struct-ptrs.c b/clang/test/AST/attr-sized-by-struct-ptrs.c new file mode 100644 index 0000000000000..5d9ed0094c685 --- /dev/null +++ b/clang/test/AST/attr-sized-by-struct-ptrs.c @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 %s -ast-dump | FileCheck %s + +#define __sized_by(f) __attribute__((sized_by(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +//============================================================================== +// __sized_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 * __sized_by(count)':'struct size_known *' +struct on_member_pointer_complete_ty { + int count; + struct size_known * buf __sized_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 * __sized_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 * __sized_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 * __sized_by(count)':'struct size_known *' +struct on_pointer_anon_buf { + int count; + struct { + struct size_known *buf __sized_by(count); + }; +}; + +struct on_pointer_anon_count { + struct { + int count; + }; + struct size_known *buf __sized_by(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 + +// 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 * __sized_by(count)':'struct size_known *' +struct on_member_pointer_complete_ty_ty_pos { + int count; + 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 **' +struct on_nested_pointer_outer { + int count; + struct size_known **__sized_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 * __sized_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 * __sized_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 * __sized_by(count)':'struct size_known *' +struct on_pointer_anon_buf_ty_pos { + int count; + struct { + struct size_known * __sized_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 *__sized_by(count) buf; +}; 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 new file mode 100644 index 0000000000000..dd3a6422521c0 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-or-null-last-field.c @@ -0,0 +1,141 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f))) + +// This has been adapted from clang/test/Sema/attr-counted-by-vla.c, but with VLAs replaced with pointers + +struct bar; + +struct not_found { + int count; + struct bar *ptr __counted_by_or_null(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 * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' field 'count' isn't within the same struct as the annotated pointer}} + } a; +}; + +struct not_found_count_not_in_unnamed_substruct { + unsigned char count; // expected-note {{'count' declared here}} + struct { + int dummy; + int * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' field 'count' isn't within the same struct as the annotated pointer}} + } a; +}; + +struct not_found_count_not_in_unnamed_substruct_2 { + struct { + unsigned char count; // expected-note {{'count' declared here}} + }; + struct { + int dummy; + int * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' field 'count' isn't within the same struct as the annotated pointer}} + } a; +}; + +struct not_found_count_in_other_unnamed_substruct { + struct { + unsigned char count; + } a1; + + struct { + int dummy; + int * ptr __counted_by_or_null(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 * ptr __counted_by_or_null(count); // expected-error {{use of undeclared identifier 'count'}} + }; +}; + +struct not_found_count_in_other_substruct_2 { + struct _a2 { + unsigned char count; + } a2; + + int * ptr __counted_by_or_null(count); // expected-error {{use of undeclared identifier 'count'}} +}; + +struct not_found_suggest { + int bork; + struct bar **ptr __counted_by_or_null(blork); // expected-error {{use of undeclared identifier 'blork'}} +}; + +int global; // expected-note {{'global' declared here}} + +struct found_outside_of_struct { + int bork; + struct bar ** ptr __counted_by_or_null(global); // expected-error {{field 'global' in 'counted_by_or_null' not inside structure}} +}; + +struct self_referrential { + int bork; + struct bar *self[] __counted_by_or_null(self); // expected-error {{use of undeclared identifier 'self'}} +}; + +struct non_int_count { + double dbl_count; + struct bar ** ptr __counted_by_or_null(dbl_count); // expected-error {{'counted_by_or_null' requires a non-boolean integer type argument}} +}; + +struct array_of_ints_count { + int integers[2]; + struct bar ** ptr __counted_by_or_null(integers); // expected-error {{'counted_by_or_null' requires a non-boolean integer type argument}} +}; + +struct not_a_c99_fam { + int count; + struct bar *non_c99_fam[0] __counted_by_or_null(count); // expected-error {{'counted_by_or_null' only applies to pointers; did you mean to use 'counted_by'?}} +}; + +struct annotated_with_anon_struct { + unsigned long flags; + struct { + unsigned char count; + int * ptr __counted_by_or_null(crount); // expected-error {{use of undeclared identifier 'crount'}} + }; +}; + +//============================================================================== +// __counted_by_or_null on a struct ptr with element type that has unknown count +//============================================================================== + +struct count_unknown; +struct on_member_ptr_incomplete_ty_ty_pos { + int count; + struct count_unknown * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct count_unknown' is an incomplete type}} +}; + +struct on_member_ptr_incomplete_const_ty_ty_pos { + int count; + const struct count_unknown * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct count_unknown' is an incomplete type}} +}; + +struct on_member_ptr_void_ty_ty_pos { + int count; + void * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}} +}; + +typedef void(fn_ty)(int); + +struct on_member_ptr_fn_ptr_ty { + int count; + fn_ty* * ptr __counted_by_or_null(count); +}; + +struct on_member_ptr_fn_ty { + int count; + fn_ty * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'fn_ty' (aka 'void (int)') is a function type}} +}; diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-off.c b/clang/test/Sema/attr-counted-by-or-null-late-parsed-off.c new file mode 100644 index 0000000000000..0e76ad9e48b4e --- /dev/null +++ b/clang/test/Sema/attr-counted-by-or-null-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_or_null(f) __attribute__((counted_by_or_null(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_or_null(count); + int count; +}; + +#else + +// ok-no-diagnostics +struct on_decl { + int count; + struct size_known *buf __counted_by_or_null(count); +}; + +#endif 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 new file mode 100644 index 0000000000000..95f517e3144f7 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c @@ -0,0 +1,255 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s + +#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f))) +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +typedef void(*fn_ptr_ty)(void); + +//============================================================================== +// __counted_by_or_null on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + struct size_known * buf __counted_by_or_null(count); + int count; +}; + +struct on_member_pointer_incomplete_ty { + struct size_unknown * buf __counted_by_or_null(count); // expected-error{{'counted_by_or_null' 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_or_null' 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_or_null(count); + int count; +}; + +struct on_member_pointer_void_ty { + void* buf __counted_by_or_null(count); // expected-error{{'counted_by_or_null' 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_or_null(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_or_null(count); + int count; +}; + +struct on_member_pointer_fn_ty { + // buffer of `count` functions is not allowed + // 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 (*fn_ptr)(void) __counted_by_or_null(count); + int count; +}; + +struct on_member_pointer_fn_ptr_ty_ty { + // buffer of `count` functions is not allowed + // 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 fn_ptr __counted_by_or_null(count); + int count; +}; + +struct has_unannotated_vla { + int count; + int buffer[]; +}; + +struct on_member_pointer_struct_with_vla { + // 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}} + struct has_unannotated_vla* objects __counted_by_or_null(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_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* objects __counted_by_or_null(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_or_null(count); + }; + int count; +}; + +struct on_pointer_anon_count { + struct size_known *buf __counted_by_or_null(count); + struct { + int 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; + 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'}} + 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'}} + 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'}} + 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'}} + 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'}} + 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'}} + 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; + }; +}; + +//============================================================================== +// __counted_by_or_null on struct non-pointer members +//============================================================================== + +struct on_pod_ty { + // expected-error-re@+1{{'counted_by_or_null' only applies to pointers{{$}}}} + int wrong_ty __counted_by_or_null(count); + int count; +}; + +struct on_void_ty { + // expected-error-re@+2{{'counted_by_or_null' only applies to pointers{{$}}}} + // expected-error@+1{{field has incomplete type 'void'}} + void wrong_ty __counted_by_or_null(count); + int count; +}; diff --git a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-sizeless-types.c b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-sizeless-types.c new file mode 100644 index 0000000000000..301977300b06a --- /dev/null +++ b/clang/test/Sema/attr-counted-by-or-null-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_or_null(f) __attribute__((counted_by_or_null(f))) + +struct on_sizeless_pointee_ty { + int count; + // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because '__SVInt8_t' is a sizeless type}} + __SVInt8_t* member __counted_by_or_null(count); +}; + +struct on_sizeless_ty { + int count; + // expected-error-re@+2{{'counted_by_or_null' only applies to pointers{{$}}}} + // expected-error@+1{{field has sizeless type '__SVInt8_t'}} + __SVInt8_t member __counted_by_or_null(count); +}; 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 new file mode 100644 index 0000000000000..017aafe0c9396 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c @@ -0,0 +1,225 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f))) +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +typedef void(*fn_ptr_ty)(void); + +//============================================================================== +// __counted_by_or_null on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + int count; + struct size_known * buf __counted_by_or_null(count); +}; + +struct on_member_pointer_incomplete_ty { + int count; + // expected-error@+1{{'counted_by_or_null' 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_or_null(count); +}; + +struct on_member_pointer_const_incomplete_ty { + int count; + // expected-error@+1{{'counted_by_or_null' 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_or_null(count); +}; + +struct on_member_pointer_void_ty { + int count; + // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}} + void* buf __counted_by_or_null(count); +}; + +struct on_member_pointer_fn_ptr_ty { + int count; + // buffer of `count` function pointers is allowed + void (**fn_ptr)(void) __counted_by_or_null(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_or_null(count); +}; + +struct on_member_pointer_fn_ty { + int count; + // buffer of `count` functions is not allowed + // 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 (*fn_ptr)(void) __counted_by_or_null(count); +}; + +struct on_member_pointer_fn_ptr_ty_ty { + int count; + // buffer of `count` functions is not allowed + // 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 fn_ptr __counted_by_or_null(count); +}; + +struct has_unannotated_vla { + int count; + int buffer[]; +}; + +struct on_member_pointer_struct_with_vla { + 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}} + struct has_unannotated_vla* objects __counted_by_or_null(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_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* objects __counted_by_or_null(count); +}; + +struct on_pointer_anon_buf { + int count; + struct { + struct size_known *buf __counted_by_or_null(count); + }; +}; + +struct on_pointer_anon_count { + struct { + int count; + }; + struct size_known *buf __counted_by_or_null(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; + struct size_known *__counted_by_or_null(count) buf; +}; + +struct on_member_pointer_incomplete_ty_ty_pos { + int count; + // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct size_unknown' is an incomplete type}} + struct size_unknown * __counted_by_or_null(count) buf; +}; + +struct on_member_pointer_const_incomplete_ty_ty_pos { + int count; + // expected-error@+1{{'counted_by_or_null' 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_or_null(count) buf; +}; + +struct on_member_pointer_void_ty_ty_pos { + int count; + // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}} + void *__counted_by_or_null(count) buf; +}; + +// - + +struct on_member_pointer_fn_ptr_ty_pos { + int count; + // buffer of `count` function pointers is allowed + void (** __counted_by_or_null(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_or_null(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_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); +}; + +struct on_member_pointer_fn_ptr_ty_ty_pos { + int count; + // buffer of `count` functions is not allowed + // 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; +}; + +// 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; + 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}} + struct has_unannotated_vla *__counted_by_or_null(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_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; +}; + +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; + struct size_known *__counted_by_or_null(count) *buf; +}; + +struct on_nested_pointer_outer { + int count; + struct size_known **__counted_by_or_null(count) buf; +}; + +struct on_pointer_anon_buf_ty_pos { + int count; + struct { + struct size_known * __counted_by_or_null(count) buf; + }; +}; + +struct on_pointer_anon_count_ty_pos { + struct { + int count; + }; + struct size_known *__counted_by_or_null(count) buf; +}; + +//============================================================================== +// __counted_by_or_null on struct non-pointer members +//============================================================================== + +struct on_pod_ty { + int count; + // expected-error-re@+1{{'counted_by_or_null' only applies to pointers{{$}}}} + int wrong_ty __counted_by_or_null(count); +}; + +struct on_void_ty { + int count; + // expected-error-re@+2{{'counted_by_or_null' only applies to pointers{{$}}}} + // expected-error@+1{{field has incomplete type 'void'}} + void wrong_ty __counted_by_or_null(count); +}; diff --git a/clang/test/Sema/attr-counted-by-or-null-vla-sizeless-types.c b/clang/test/Sema/attr-counted-by-or-null-vla-sizeless-types.c new file mode 100644 index 0000000000000..8abd4476fe597 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-or-null-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_or_null(f) __attribute__((counted_by_or_null(f))) + +struct on_sizeless_elt_ty { + int count; + // expected-error-re@+2{{'counted_by_or_null' only applies to pointers{{$}}}} + // expected-error@+1{{array has sizeless element type '__SVInt8_t'}} + __SVInt8_t arr[] __counted_by_or_null(count); +}; diff --git a/clang/test/Sema/attr-counted-by-vla.c b/clang/test/Sema/attr-counted-by-vla.c index b25f719f3b95a..571d6e6291e6b 100644 --- a/clang/test/Sema/attr-counted-by-vla.c +++ b/clang/test/Sema/attr-counted-by-vla.c @@ -14,7 +14,7 @@ struct no_found_count_not_in_substruct { 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}} + int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the annotated flexible array}} } a; }; @@ -22,7 +22,7 @@ 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}} + int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the annotated flexible array}} } a; }; @@ -32,7 +32,7 @@ struct not_found_count_not_in_unnamed_substruct_2 { }; struct { int dummy; - int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}} + int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the annotated flexible array}} } a; }; diff --git a/clang/test/Sema/attr-sized-by-last-field.c b/clang/test/Sema/attr-sized-by-last-field.c new file mode 100644 index 0000000000000..6af29e9f31435 --- /dev/null +++ b/clang/test/Sema/attr-sized-by-last-field.c @@ -0,0 +1,142 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __sized_by(f) __attribute__((sized_by(f))) + +// This has been adapted from clang/test/Sema/attr-counted-by-vla.c, but with VLAs replaced with pointers + +struct bar; + +struct not_found { + int size; + struct bar *ptr __sized_by(bork); // expected-error {{use of undeclared identifier 'bork'}} +}; + +struct no_found_size_not_in_substruct { + unsigned long flags; + unsigned char size; // expected-note {{'size' declared here}} + struct A { + int dummy; + int * ptr __sized_by(size); // expected-error {{'sized_by' field 'size' isn't within the same struct as the annotated pointer}} + } a; +}; + +struct not_found_size_not_in_unnamed_substruct { + unsigned char size; // expected-note {{'size' declared here}} + struct { + int dummy; + int * ptr __sized_by(size); // expected-error {{'sized_by' field 'size' isn't within the same struct as the annotated pointer}} + } a; +}; + +struct not_found_size_not_in_unnamed_substruct_2 { + struct { + unsigned char size; // expected-note {{'size' declared here}} + }; + struct { + int dummy; + int * ptr __sized_by(size); // expected-error {{'sized_by' field 'size' isn't within the same struct as the annotated pointer}} + } a; +}; + +struct not_found_size_in_other_unnamed_substruct { + struct { + unsigned char size; + } a1; + + struct { + int dummy; + int * ptr __sized_by(size); // expected-error {{use of undeclared identifier 'size'}} + }; +}; + +struct not_found_size_in_other_substruct { + struct _a1 { + unsigned char size; + } a1; + + struct { + int dummy; + int * ptr __sized_by(size); // expected-error {{use of undeclared identifier 'size'}} + }; +}; + +struct not_found_size_in_other_substruct_2 { + struct _a2 { + unsigned char size; + } a2; + + int * ptr __sized_by(size); // expected-error {{use of undeclared identifier 'size'}} +}; + +struct not_found_suggest { + int bork; + struct bar **ptr __sized_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 ** ptr __sized_by(global); // expected-error {{field 'global' in 'sized_by' not inside structure}} +}; + +struct self_referrential { + int bork; + struct bar *self[] __sized_by(self); // expected-error {{use of undeclared identifier 'self'}} +}; + +struct non_int_size { + double dbl_size; + struct bar ** ptr __sized_by(dbl_size); // expected-error {{'sized_by' requires a non-boolean integer type argument}} +}; + +struct array_of_ints_size { + int integers[2]; + struct bar ** ptr __sized_by(integers); // expected-error {{'sized_by' requires a non-boolean integer type argument}} +}; + +struct not_a_c99_fam { + int size; + struct bar *non_c99_fam[0] __sized_by(size); // expected-error {{'sized_by' only applies to pointers; did you mean to use 'counted_by'?}} +}; + +struct annotated_with_anon_struct { + unsigned long flags; + struct { + unsigned char size; + int * ptr __sized_by(crount); // expected-error {{use of undeclared identifier 'crount'}} + }; +}; + +//============================================================================== +// __sized_by on a struct ptr with element type that has unknown size +//============================================================================== + +struct size_unknown; +struct on_member_ptr_incomplete_ty_ty_pos { + int size; + struct size_unknown * ptr __sized_by(size); +}; + +struct on_member_ptr_incomplete_const_ty_ty_pos { + int size; + const struct size_unknown * ptr __sized_by(size); +}; + +struct on_member_ptr_void_ty_ty_pos { + int size; + void * ptr __sized_by(size); +}; + +typedef void(fn_ty)(int); + +struct on_member_ptr_fn_ptr_ty { + int size; + fn_ty* * ptr __sized_by(size); +}; + +struct on_member_ptr_fn_ty { + int size; + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'fn_ty' (aka 'void (int)') is a function type}} + fn_ty * ptr __sized_by(size); +}; diff --git a/clang/test/Sema/attr-sized-by-late-parsed-off.c b/clang/test/Sema/attr-sized-by-late-parsed-off.c new file mode 100644 index 0000000000000..e43125c8ce2f9 --- /dev/null +++ b/clang/test/Sema/attr-sized-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 __sized_by(f) __attribute__((sized_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 __sized_by(count); + int count; +}; + +#else + +// ok-no-diagnostics +struct on_decl { + int count; + struct size_known *buf __sized_by(count); +}; + +#endif 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 new file mode 100644 index 0000000000000..07f8801787d66 --- /dev/null +++ b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c @@ -0,0 +1,247 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s + +#define __sized_by(f) __attribute__((sized_by(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +typedef void(*fn_ptr_ty)(void); + +//============================================================================== +// __sized_by on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + struct size_known * buf __sized_by(size); + int size; +}; + +struct on_member_pointer_incomplete_ty { + struct size_unknown * buf __sized_by(size); + int size; +}; + +struct on_member_pointer_const_incomplete_ty { + const struct size_unknown * buf __sized_by(size); + int size; +}; + +struct on_member_pointer_void_ty { + void* buf __sized_by(size); + int size; +}; + +struct on_member_pointer_fn_ptr_ty { + // buffer of `size` function pointers is allowed + void (**fn_ptr)(void) __sized_by(size); + int size; +}; + + +struct on_member_pointer_fn_ptr_ty_ptr_ty { + // buffer of `size` function pointers is allowed + fn_ptr_ty* fn_ptr __sized_by(size); + int size; +}; + +struct on_member_pointer_fn_ty { + // buffer of function(s) with size `size` is allowed + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} + void (*fn_ptr)(void) __sized_by(size); + int size; +}; + +struct on_member_pointer_fn_ptr_ty_ty { + // buffer of function(s) with size `size` is allowed + // 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 fn_ptr __sized_by(size); + int size; +}; + +struct has_unannotated_vla { + int size; + int buffer[]; +}; + +struct on_member_pointer_struct_with_vla { + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}} + struct has_unannotated_vla* objects __sized_by(size); + int size; +}; + +struct has_annotated_vla { + int size; + // expected-error@+1{{'sized_by' only applies to pointers; did you mean to use 'counted_by'?}} + int buffer[] __sized_by(size); +}; + +struct on_member_pointer_struct_with_annotated_vla { + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}} + struct has_annotated_vla* objects __sized_by(size); + int size; +}; + +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; +}; + +struct on_pointer_anon_count { + struct size_known *buf __sized_by(size); + struct { + int size; + }; +}; + +//============================================================================== +// __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; +}; + +// - + +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'}} + 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'}} + 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'}} + 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'}} + 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; + }; +}; + +//============================================================================== +// __sized_by on struct non-pointer members +//============================================================================== + +struct on_pod_ty { + // expected-error-re@+1{{'sized_by' only applies to pointers{{$}}}} + int wrong_ty __sized_by(size); + int size; +}; + +struct on_void_ty { + // expected-error-re@+2{{'sized_by' only applies to pointers{{$}}}} + // expected-error@+1{{field has incomplete type 'void'}} + void wrong_ty __sized_by(size); + int size; +}; diff --git a/clang/test/Sema/attr-sized-by-or-null-last-field.c b/clang/test/Sema/attr-sized-by-or-null-last-field.c new file mode 100644 index 0000000000000..96bbe847b910b --- /dev/null +++ b/clang/test/Sema/attr-sized-by-or-null-last-field.c @@ -0,0 +1,142 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __sized_by_or_null(f) __attribute__((sized_by_or_null(f))) + +// This has been adapted from clang/test/Sema/attr-counted-by-vla.c, but with VLAs replaced with pointers + +struct bar; + +struct not_found { + int size; + struct bar *ptr __sized_by_or_null(bork); // expected-error {{use of undeclared identifier 'bork'}} +}; + +struct no_found_size_not_in_substruct { + unsigned long flags; + unsigned char size; // expected-note {{'size' declared here}} + struct A { + int dummy; + int * ptr __sized_by_or_null(size); // expected-error {{'sized_by_or_null' field 'size' isn't within the same struct as the annotated pointer}} + } a; +}; + +struct not_found_size_not_in_unnamed_substruct { + unsigned char size; // expected-note {{'size' declared here}} + struct { + int dummy; + int * ptr __sized_by_or_null(size); // expected-error {{'sized_by_or_null' field 'size' isn't within the same struct as the annotated pointer}} + } a; +}; + +struct not_found_size_not_in_unnamed_substruct_2 { + struct { + unsigned char size; // expected-note {{'size' declared here}} + }; + struct { + int dummy; + int * ptr __sized_by_or_null(size); // expected-error {{'sized_by_or_null' field 'size' isn't within the same struct as the annotated pointer}} + } a; +}; + +struct not_found_size_in_other_unnamed_substruct { + struct { + unsigned char size; + } a1; + + struct { + int dummy; + int * ptr __sized_by_or_null(size); // expected-error {{use of undeclared identifier 'size'}} + }; +}; + +struct not_found_size_in_other_substruct { + struct _a1 { + unsigned char size; + } a1; + + struct { + int dummy; + int * ptr __sized_by_or_null(size); // expected-error {{use of undeclared identifier 'size'}} + }; +}; + +struct not_found_size_in_other_substruct_2 { + struct _a2 { + unsigned char size; + } a2; + + int * ptr __sized_by_or_null(size); // expected-error {{use of undeclared identifier 'size'}} +}; + +struct not_found_suggest { + int bork; + struct bar **ptr __sized_by_or_null(blork); // expected-error {{use of undeclared identifier 'blork'}} +}; + +int global; // expected-note {{'global' declared here}} + +struct found_outside_of_struct { + int bork; + struct bar ** ptr __sized_by_or_null(global); // expected-error {{field 'global' in 'sized_by_or_null' not inside structure}} +}; + +struct self_referrential { + int bork; + struct bar *self[] __sized_by_or_null(self); // expected-error {{use of undeclared identifier 'self'}} +}; + +struct non_int_size { + double dbl_size; + struct bar ** ptr __sized_by_or_null(dbl_size); // expected-error {{'sized_by_or_null' requires a non-boolean integer type argument}} +}; + +struct array_of_ints_size { + int integers[2]; + struct bar ** ptr __sized_by_or_null(integers); // expected-error {{'sized_by_or_null' requires a non-boolean integer type argument}} +}; + +struct not_a_c99_fam { + int size; + struct bar *non_c99_fam[0] __sized_by_or_null(size); // expected-error {{'sized_by_or_null' only applies to pointers; did you mean to use 'counted_by'?}} +}; + +struct annotated_with_anon_struct { + unsigned long flags; + struct { + unsigned char size; + int * ptr __sized_by_or_null(crount); // expected-error {{use of undeclared identifier 'crount'}} + }; +}; + +//============================================================================== +// __sized_by_or_null on a struct ptr with element type that has unknown size +//============================================================================== + +struct size_unknown; +struct on_member_ptr_incomplete_ty_ty_pos { + int size; + struct size_unknown * ptr __sized_by_or_null(size); +}; + +struct on_member_ptr_incomplete_const_ty_ty_pos { + int size; + const struct size_unknown * ptr __sized_by_or_null(size); +}; + +struct on_member_ptr_void_ty_ty_pos { + int size; + void * ptr __sized_by_or_null(size); +}; + +typedef void(fn_ty)(int); + +struct on_member_ptr_fn_ptr_ty { + int size; + fn_ty* * ptr __sized_by_or_null(size); +}; + +struct on_member_ptr_fn_ty { + int size; + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'fn_ty' (aka 'void (int)') is a function type}} + fn_ty * ptr __sized_by_or_null(size); +}; diff --git a/clang/test/Sema/attr-sized-by-or-null-late-parsed-off.c b/clang/test/Sema/attr-sized-by-or-null-late-parsed-off.c new file mode 100644 index 0000000000000..8bc775f196c18 --- /dev/null +++ b/clang/test/Sema/attr-sized-by-or-null-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 __sized_by_or_null(f) __attribute__((sized_by_or_null(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 __sized_by_or_null(count); + int count; +}; + +#else + +// ok-no-diagnostics +struct on_decl { + int count; + struct size_known *buf __sized_by_or_null(count); +}; + +#endif 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 new file mode 100644 index 0000000000000..afe5f0af28083 --- /dev/null +++ b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c @@ -0,0 +1,247 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s + +#define __sized_by_or_null(f) __attribute__((__sized_by_or_null__(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +typedef void(*fn_ptr_ty)(void); + +//============================================================================== +// __sized_by_or_null on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + struct size_known * buf __sized_by_or_null(size); + int size; +}; + +struct on_member_pointer_incomplete_ty { + struct size_unknown * buf __sized_by_or_null(size); + int size; +}; + +struct on_member_pointer_const_incomplete_ty { + const struct size_unknown * buf __sized_by_or_null(size); + int size; +}; + +struct on_member_pointer_void_ty { + void* buf __sized_by_or_null(size); + int size; +}; + +struct on_member_pointer_fn_ptr_ty { + // buffer of `size` function pointers is allowed + void (**fn_ptr)(void) __sized_by_or_null(size); + int size; +}; + + +struct on_member_pointer_fn_ptr_ty_ptr_ty { + // buffer of `size` function pointers is allowed + fn_ptr_ty* fn_ptr __sized_by_or_null(size); + int size; +}; + +struct on_member_pointer_fn_ty { + // buffer of function(s) with size `size` is allowed + // 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 (*fn_ptr)(void) __sized_by_or_null(size); + int size; +}; + +struct on_member_pointer_fn_ptr_ty_ty { + // buffer of function(s) with size `size` is allowed + // 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 fn_ptr __sized_by_or_null(size); + int size; +}; + +struct has_unannotated_vla { + int size; + int buffer[]; +}; + +struct on_member_pointer_struct_with_vla { + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}} + struct has_unannotated_vla* objects __sized_by_or_null(size); + int size; +}; + +struct has_annotated_vla { + int size; + // expected-error@+1{{'sized_by_or_null' only applies to pointers; did you mean to use 'counted_by'?}} + int buffer[] __sized_by_or_null(size); +}; + +struct on_member_pointer_struct_with_annotated_vla { + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}} + struct has_annotated_vla* objects __sized_by_or_null(size); + int size; +}; + +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; +}; + +struct on_pointer_anon_count { + struct size_known *buf __sized_by_or_null(size); + struct { + int 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 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; +}; + +// - + +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'}} + 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'}} + 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'}} + 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'}} + 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; + }; +}; + +//============================================================================== +// __sized_by_or_null on struct non-pointer members +//============================================================================== + +struct on_pod_ty { + // expected-error-re@+1{{'sized_by_or_null' only applies to pointers{{$}}}} + int wrong_ty __sized_by_or_null(size); + int size; +}; + +struct on_void_ty { + // expected-error-re@+2{{'sized_by_or_null' only applies to pointers{{$}}}} + // expected-error@+1{{field has incomplete type 'void'}} + void wrong_ty __sized_by_or_null(size); + int size; +}; diff --git a/clang/test/Sema/attr-sized-by-or-null-struct-ptrs-sizeless-types.c b/clang/test/Sema/attr-sized-by-or-null-struct-ptrs-sizeless-types.c new file mode 100644 index 0000000000000..4a360b9722a0b --- /dev/null +++ b/clang/test/Sema/attr-sized-by-or-null-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 __sized_by_or_null(f) __attribute__((sized_by_or_null(f))) + +struct on_sizeless_pointee_ty { + int size; + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because '__SVInt8_t' is a sizeless type}} + __SVInt8_t* member __sized_by_or_null(size); +}; + +struct on_sizeless_ty { + int size; + // expected-error-re@+2{{'sized_by_or_null' only applies to pointers{{$}}}} + // expected-error@+1{{field has sizeless type '__SVInt8_t'}} + __SVInt8_t member __sized_by_or_null(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 new file mode 100644 index 0000000000000..2c7578b5ecbe6 --- /dev/null +++ b/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c @@ -0,0 +1,219 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __sized_by_or_null(f) __attribute__((sized_by_or_null(f))) +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +typedef void(*fn_ptr_ty)(void); + +//============================================================================== +// __sized_by_or_null on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + int size; + struct size_known * buf __sized_by_or_null(size); +}; + +struct on_member_pointer_incomplete_ty { + int size; + struct size_unknown * buf __sized_by_or_null(size); +}; + +struct on_member_pointer_const_incomplete_ty { + int size; + const struct size_unknown * buf __sized_by_or_null(size); +}; + +struct on_member_pointer_void_ty { + int size; + void* buf __sized_by_or_null(size); +}; + +struct on_member_pointer_fn_ptr_ty { + int size; + // buffer of function pointers with size `size` is allowed + void (**fn_ptr)(void) __sized_by_or_null(size); +}; + +struct on_member_pointer_fn_ptr_ty_ptr_ty { + int size; + // buffer of function pointers with size `size` is allowed + fn_ptr_ty* fn_ptr __sized_by_or_null(size); +}; + +struct on_member_pointer_fn_ty { + int size; + // buffer of functions with size `size` is allowed + // 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 (*fn_ptr)(void) __sized_by_or_null(size); +}; + +struct on_member_pointer_fn_ptr_ty_ty { + int size; + // buffer of functions with size `size` is allowed + // 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 fn_ptr __sized_by_or_null(size); +}; + +struct has_unannotated_vla { + int count; + int buffer[]; +}; + +struct on_member_pointer_struct_with_vla { + int size; + // we know the size so this is fine for tracking size, however indexing would be an issue + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}} + struct has_unannotated_vla* objects __sized_by_or_null(size); +}; + +struct has_annotated_vla { + int count; + int buffer[] __counted_by(count); +}; + +struct on_member_pointer_struct_with_annotated_vla { + int size; + // we know the size so this is fine for tracking size, however indexing would be an issue + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}} + struct has_annotated_vla* objects __sized_by_or_null(size); +}; + +struct on_pointer_anon_buf { + int size; + struct { + struct size_known *buf __sized_by_or_null(size); + }; +}; + +struct on_pointer_anon_size { + struct { + int size; + }; + struct size_known *buf __sized_by_or_null(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; + struct size_known *__sized_by_or_null(size) buf; +}; + +struct on_member_pointer_incomplete_ty_ty_pos { + int size; + struct size_unknown * __sized_by_or_null(size) buf; +}; + +struct on_member_pointer_const_incomplete_ty_ty_pos { + int size; + const struct size_unknown * __sized_by_or_null(size) buf; +}; + +struct on_member_pointer_void_ty_ty_pos { + int size; + void *__sized_by_or_null(size) buf; +}; + +// - + +struct on_member_pointer_fn_ptr_ty_pos { + int size; + // buffer of `size` function pointers is allowed + void (** __sized_by_or_null(size) fn_ptr)(void); +}; + +struct on_member_pointer_fn_ptr_ty_ptr_ty_pos { + int size; + // buffer of `size` function pointers is allowed + fn_ptr_ty* __sized_by_or_null(size) fn_ptr; +}; + +struct on_member_pointer_fn_ty_ty_pos { + int size; + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} + void (* __sized_by_or_null(size) fn_ptr)(void); +}; + +struct on_member_pointer_fn_ptr_ty_ty_pos { + int 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; +}; + +// 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; + void (* __sized_by_or_null(size) * fn_ptr)(void); +}; + +struct on_member_pointer_struct_with_vla_ty_pos { + int size; + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}} + struct has_unannotated_vla *__sized_by_or_null(size) objects; +}; + +struct on_member_pointer_struct_with_annotated_vla_ty_pos { + int size; + // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}} + struct has_annotated_vla* __sized_by_or_null(size) objects; +}; + +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; + struct size_known *__sized_by_or_null(size) *buf; +}; + +struct on_nested_pointer_outer { + int size; + struct size_known **__sized_by_or_null(size) buf; +}; + +struct on_pointer_anon_buf_ty_pos { + int size; + struct { + struct size_known * __sized_by_or_null(size) buf; + }; +}; + +struct on_pointer_anon_size_ty_pos { + struct { + int size; + }; + struct size_known *__sized_by_or_null(size) buf; +}; + +//============================================================================== +// __sized_by_or_null on struct non-pointer members +//============================================================================== + +struct on_pod_ty { + int size; + // expected-error-re@+1{{'sized_by_or_null' only applies to pointers{{$}}}} + int wrong_ty __sized_by_or_null(size); +}; + +struct on_void_ty { + int size; + // expected-error-re@+2{{'sized_by_or_null' only applies to pointers{{$}}}} + // expected-error@+1{{field has incomplete type 'void'}} + void wrong_ty __sized_by_or_null(size); +}; + +struct on_member_array_complete_ty { + int size; + // expected-error@+1{{'sized_by_or_null' only applies to pointers; did you mean to use 'counted_by'?}} + struct size_known array[] __sized_by_or_null(size); +}; diff --git a/clang/test/Sema/attr-sized-by-or-null-vla-sizeless-types.c b/clang/test/Sema/attr-sized-by-or-null-vla-sizeless-types.c new file mode 100644 index 0000000000000..398b1df592fe3 --- /dev/null +++ b/clang/test/Sema/attr-sized-by-or-null-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 __sized_by_or_null(f) __attribute__((sized_by_or_null(f))) + +struct on_sizeless_elt_ty { + int count; + // expected-error-re@+2{{'sized_by_or_null' only applies to pointers{{$}}}} + // expected-error@+1{{array has sizeless element type '__SVInt8_t'}} + __SVInt8_t arr[] __sized_by_or_null(count); +}; diff --git a/clang/test/Sema/attr-sized-by-struct-ptrs-sizeless-types.c b/clang/test/Sema/attr-sized-by-struct-ptrs-sizeless-types.c new file mode 100644 index 0000000000000..2e916bdb04720 --- /dev/null +++ b/clang/test/Sema/attr-sized-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 __sized_by(f) __attribute__((sized_by(f))) + +struct on_sizeless_pointee_ty { + int count; + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because '__SVInt8_t' is a sizeless type}} + __SVInt8_t* member __sized_by(count); +}; + +struct on_sizeless_ty { + int count; + // expected-error-re@+2{{'sized_by' only applies to pointers{{$}}}} + // expected-error@+1{{field has sizeless type '__SVInt8_t'}} + __SVInt8_t member __sized_by(count); +}; diff --git a/clang/test/Sema/attr-sized-by-struct-ptrs.c b/clang/test/Sema/attr-sized-by-struct-ptrs.c new file mode 100644 index 0000000000000..01195469c6fe4 --- /dev/null +++ b/clang/test/Sema/attr-sized-by-struct-ptrs.c @@ -0,0 +1,219 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __sized_by(f) __attribute__((sized_by(f))) +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_unknown; +struct size_known { + int field; +}; + +typedef void(*fn_ptr_ty)(void); + +//============================================================================== +// __sized_by on struct member pointer in decl attribute position +//============================================================================== + +struct on_member_pointer_complete_ty { + int size; + struct size_known * buf __sized_by(size); +}; + +struct on_member_pointer_incomplete_ty { + int size; + struct size_unknown * buf __sized_by(size); +}; + +struct on_member_pointer_const_incomplete_ty { + int size; + const struct size_unknown * buf __sized_by(size); +}; + +struct on_member_pointer_void_ty { + int size; + void* buf __sized_by(size); +}; + +struct on_member_pointer_fn_ptr_ty { + int size; + // buffer of function pointers with size `size` is allowed + void (**fn_ptr)(void) __sized_by(size); +}; + +struct on_member_pointer_fn_ptr_ty_ptr_ty { + int size; + // buffer of function pointers with size `size` is allowed + fn_ptr_ty* fn_ptr __sized_by(size); +}; + +struct on_member_pointer_fn_ty { + int size; + // buffer of functions with size `size` is allowed + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'void (void)' is a function type}} + void (*fn_ptr)(void) __sized_by(size); +}; + +struct on_member_pointer_fn_ptr_ty_ty { + int size; + // buffer of functions with size `size` is allowed + // 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 fn_ptr __sized_by(size); +}; + +struct has_unannotated_vla { + int count; + int buffer[]; +}; + +struct on_member_pointer_struct_with_vla { + int size; + // we know the size so this is fine for tracking size, however indexing would be an issue + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}} + struct has_unannotated_vla* objects __sized_by(size); +}; + +struct has_annotated_vla { + int count; + int buffer[] __counted_by(count); +}; + +struct on_member_pointer_struct_with_annotated_vla { + int size; + // we know the size so this is fine for tracking size, however indexing would be an issue + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}} + struct has_annotated_vla* objects __sized_by(size); +}; + +struct on_pointer_anon_buf { + int size; + struct { + struct size_known *buf __sized_by(size); + }; +}; + +struct on_pointer_anon_size { + struct { + int size; + }; + struct size_known *buf __sized_by(size); +}; + +//============================================================================== +// __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 + +struct on_member_pointer_complete_ty_ty_pos { + int size; + struct size_known *__sized_by(size) buf; +}; + +struct on_member_pointer_incomplete_ty_ty_pos { + int size; + struct size_unknown * __sized_by(size) buf; +}; + +struct on_member_pointer_const_incomplete_ty_ty_pos { + int size; + const struct size_unknown * __sized_by(size) buf; +}; + +struct on_member_pointer_void_ty_ty_pos { + int size; + void *__sized_by(size) buf; +}; + +// - + +struct on_member_pointer_fn_ptr_ty_pos { + int size; + // buffer of `size` function pointers is allowed + void (** __sized_by(size) fn_ptr)(void); +}; + +struct on_member_pointer_fn_ptr_ty_ptr_ty_pos { + int size; + // buffer of `size` function pointers is allowed + fn_ptr_ty* __sized_by(size) fn_ptr; +}; + +struct on_member_pointer_fn_ty_ty_pos { + int 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); +}; + +struct on_member_pointer_fn_ptr_ty_ty_pos { + int 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; +}; + +// 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; + void (* __sized_by(size) * fn_ptr)(void); +}; + +struct on_member_pointer_struct_with_vla_ty_pos { + int size; + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_unannotated_vla' is a struct type with a flexible array member}} + struct has_unannotated_vla *__sized_by(size) objects; +}; + +struct on_member_pointer_struct_with_annotated_vla_ty_pos { + int size; + // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee of unknown size because 'struct has_annotated_vla' is a struct type with a flexible array member}} + struct has_annotated_vla* __sized_by(size) objects; +}; + +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; + struct size_known *__sized_by(size) *buf; +}; + +struct on_nested_pointer_outer { + int size; + struct size_known **__sized_by(size) buf; +}; + +struct on_pointer_anon_buf_ty_pos { + int size; + struct { + struct size_known * __sized_by(size) buf; + }; +}; + +struct on_pointer_anon_size_ty_pos { + struct { + int size; + }; + struct size_known *__sized_by(size) buf; +}; + +//============================================================================== +// __sized_by on struct non-pointer members +//============================================================================== + +struct on_pod_ty { + int size; + // expected-error-re@+1{{'sized_by' only applies to pointers{{$}}}} + int wrong_ty __sized_by(size); +}; + +struct on_void_ty { + int size; + // expected-error-re@+2{{'sized_by' only applies to pointers{{$}}}} + // expected-error@+1{{field has incomplete type 'void'}} + void wrong_ty __sized_by(size); +}; + +struct on_member_array_complete_ty { + int size; + // expected-error@+1{{'sized_by' only applies to pointers; did you mean to use 'counted_by'?}} + struct size_known array[] __sized_by(size); +}; diff --git a/clang/test/Sema/attr-sized-by-vla-sizeless-types.c b/clang/test/Sema/attr-sized-by-vla-sizeless-types.c new file mode 100644 index 0000000000000..37e91639bb4a1 --- /dev/null +++ b/clang/test/Sema/attr-sized-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 __sized_by(f) __attribute__((sized_by(f))) + +struct on_sizeless_elt_ty { + int count; + // expected-error-re@+2{{'sized_by' only applies to pointers{{$}}}} + // expected-error@+1{{array has sizeless element type '__SVInt8_t'}} + __SVInt8_t arr[] __sized_by(count); +};