[Bounds-Safety] Add sized_by, counted_by_or_null & sized_by_or_null (#93231)
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. rdar://125400354
This commit is contained in:
parent
9e1f1cfa59
commit
e22ebee5a3
@ -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
|
||||
|
@ -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".
|
||||
|
@ -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<ObjCFlexibleArray>;
|
||||
|
||||
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{"
|
||||
|
@ -14603,7 +14603,9 @@ public:
|
||||
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
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
@ -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<TypeCoupledDeclRefInfo> &Decls) {
|
||||
static bool
|
||||
CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E,
|
||||
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &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<DeclRefExpr>(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<TypeCoupledDeclRefInfo, 1> 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;
|
||||
|
||||
|
@ -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<TypeCoupledDeclRefInfo, 1> 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
|
||||
|
@ -7397,7 +7397,8 @@ QualType TreeTransform<Derived>::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<CountAttributedTypeLoc>(Result);
|
||||
|
@ -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.
|
117
clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
Normal file
117
clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
Normal file
@ -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;
|
||||
};
|
45
clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c
Normal file
45
clang/test/AST/attr-sized-by-late-parsed-struct-ptrs.c
Normal file
@ -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.
|
@ -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.
|
117
clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
Normal file
117
clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
Normal file
@ -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;
|
||||
};
|
117
clang/test/AST/attr-sized-by-struct-ptrs.c
Normal file
117
clang/test/AST/attr-sized-by-struct-ptrs.c
Normal file
@ -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;
|
||||
};
|
141
clang/test/Sema/attr-counted-by-or-null-last-field.c
Normal file
141
clang/test/Sema/attr-counted-by-or-null-last-field.c
Normal file
@ -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}}
|
||||
};
|
26
clang/test/Sema/attr-counted-by-or-null-late-parsed-off.c
Normal file
26
clang/test/Sema/attr-counted-by-or-null-late-parsed-off.c
Normal file
@ -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
|
@ -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;
|
||||
};
|
@ -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);
|
||||
};
|
225
clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
Normal file
225
clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
Normal file
@ -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);
|
||||
};
|
11
clang/test/Sema/attr-counted-by-or-null-vla-sizeless-types.c
Normal file
11
clang/test/Sema/attr-counted-by-or-null-vla-sizeless-types.c
Normal file
@ -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);
|
||||
};
|
@ -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;
|
||||
};
|
||||
|
||||
|
142
clang/test/Sema/attr-sized-by-last-field.c
Normal file
142
clang/test/Sema/attr-sized-by-last-field.c
Normal file
@ -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);
|
||||
};
|
26
clang/test/Sema/attr-sized-by-late-parsed-off.c
Normal file
26
clang/test/Sema/attr-sized-by-late-parsed-off.c
Normal file
@ -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
|
247
clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
Normal file
247
clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
Normal file
@ -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;
|
||||
};
|
142
clang/test/Sema/attr-sized-by-or-null-last-field.c
Normal file
142
clang/test/Sema/attr-sized-by-or-null-last-field.c
Normal file
@ -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);
|
||||
};
|
26
clang/test/Sema/attr-sized-by-or-null-late-parsed-off.c
Normal file
26
clang/test/Sema/attr-sized-by-or-null-late-parsed-off.c
Normal file
@ -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
|
247
clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
Normal file
247
clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
Normal file
@ -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;
|
||||
};
|
@ -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);
|
||||
};
|
219
clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
Normal file
219
clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
Normal file
@ -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);
|
||||
};
|
11
clang/test/Sema/attr-sized-by-or-null-vla-sizeless-types.c
Normal file
11
clang/test/Sema/attr-sized-by-or-null-vla-sizeless-types.c
Normal file
@ -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);
|
||||
};
|
17
clang/test/Sema/attr-sized-by-struct-ptrs-sizeless-types.c
Normal file
17
clang/test/Sema/attr-sized-by-struct-ptrs-sizeless-types.c
Normal file
@ -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);
|
||||
};
|
219
clang/test/Sema/attr-sized-by-struct-ptrs.c
Normal file
219
clang/test/Sema/attr-sized-by-struct-ptrs.c
Normal file
@ -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);
|
||||
};
|
11
clang/test/Sema/attr-sized-by-vla-sizeless-types.c
Normal file
11
clang/test/Sema/attr-sized-by-vla-sizeless-types.c
Normal file
@ -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);
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user