[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:
Henrik G. Olsson 2024-07-09 13:58:01 -07:00 committed by GitHub
parent 9e1f1cfa59
commit e22ebee5a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 2628 additions and 51 deletions

View File

@ -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

View File

@ -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".

View File

@ -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{"

View File

@ -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

View File

@ -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:

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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.

View 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;
};

View 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.

View File

@ -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.

View 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;
};

View 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;
};

View 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}}
};

View 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

View File

@ -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;
};

View 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 __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);
};

View 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);
};

View 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);
};

View File

@ -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;
};

View 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);
};

View 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

View 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;
};

View 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);
};

View 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

View 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;
};

View 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_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);
};

View 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);
};

View 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);
};

View 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);
};

View 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);
};

View 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);
};