95 changes: 15 additions & 80 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8633,95 +8633,31 @@ static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) {
return RD;
}

enum class CountedByInvalidPointeeTypeKind {
INCOMPLETE,
SIZELESS,
FUNCTION,
FLEXIBLE_ARRAY_MEMBER,
VALID,
};

static bool CheckCountedByAttrOnField(
Sema &S, FieldDecl *FD, Expr *E,
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) {
// Check the context the attribute is used in

static bool
CheckCountExpr(Sema &S, FieldDecl *FD, Expr *E,
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) {
if (FD->getParent()->isUnion()) {
S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union)
<< FD->getSourceRange();
return true;
}

const auto FieldTy = FD->getType();
if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
S.Diag(FD->getBeginLoc(),
diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
<< FD->getLocation();
if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
S.Diag(E->getBeginLoc(), diag::err_counted_by_attr_argument_not_integer)
<< E->getSourceRange();
return true;
}

LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
LangOptions::StrictFlexArraysLevelKind::IncompleteOnly;
if (FieldTy->isArrayType() &&
!Decl::isFlexibleArrayMemberLike(S.getASTContext(), FD, FieldTy,
StrictFlexArraysLevel, true)) {
S.Diag(FD->getBeginLoc(),
diag::err_counted_by_attr_on_array_not_flexible_array_member)
<< FD->getLocation();
return true;
}

CountedByInvalidPointeeTypeKind InvalidTypeKind =
CountedByInvalidPointeeTypeKind::VALID;
QualType PointeeTy;
int SelectPtrOrArr = 0;
if (FieldTy->isPointerType()) {
PointeeTy = FieldTy->getPointeeType();
SelectPtrOrArr = 0;
} else {
assert(FieldTy->isArrayType());
const ArrayType *AT = S.getASTContext().getAsArrayType(FieldTy);
PointeeTy = AT->getElementType();
SelectPtrOrArr = 1;
}
// Note: The `Decl::isFlexibleArrayMemberLike` check earlier on means
// only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
// when `FieldTy->isArrayType()`.
bool ShouldWarn = false;
if (PointeeTy->isIncompleteType()) {
InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
} else if (PointeeTy->isSizelessType()) {
InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
} else if (PointeeTy->isFunctionType()) {
InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;
} else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {
if (FieldTy->isArrayType()) {
// This is a workaround for the Linux kernel that has already adopted
// `counted_by` on a FAM where the pointee is a struct with a FAM. This
// should be an error because computing the bounds of the array cannot be
// done correctly without manually traversing every struct object in the
// array at runtime. To allow the code to be built this error is
// downgraded to a warning.
ShouldWarn = true;
}
InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
}

if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
unsigned DiagID = ShouldWarn
? diag::warn_counted_by_attr_elt_type_unknown_size
: diag::err_counted_by_attr_pointee_unknown_size;
S.Diag(FD->getBeginLoc(), DiagID)
<< SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
<< (ShouldWarn ? 1 : 0) << FD->getSourceRange();
return true;
}

// Check the expression

if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
S.Diag(E->getBeginLoc(), diag::err_counted_by_attr_argument_not_integer)
<< E->getSourceRange();
if (!Decl::isFlexibleArrayMemberLike(S.getASTContext(), FD, FD->getType(),
StrictFlexArraysLevel, true)) {
// The "counted_by" attribute must be on a flexible array member.
SourceRange SR = FD->getLocation();
S.Diag(SR.getBegin(),
diag::err_counted_by_attr_not_on_flexible_array_member)
<< SR;
return true;
}

Expand Down Expand Up @@ -8784,11 +8720,10 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
return;

llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls))
if (CheckCountExpr(S, FD, CountExpr, Decls))
return;

QualType CAT =
S.BuildCountAttributedArrayOrPointerType(FD->getType(), CountExpr);
QualType CAT = S.BuildCountAttributedArrayType(FD->getType(), CountExpr);
FD->setType(CAT);
}

Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9345,9 +9345,9 @@ BuildTypeCoupledDecls(Expr *E,
Decls.push_back(TypeCoupledDeclRefInfo(CountDecl, /*IsDref*/ false));
}

QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
Expr *CountExpr) {
assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType());
QualType Sema::BuildCountAttributedArrayType(QualType WrappedTy,
Expr *CountExpr) {
assert(WrappedTy->isIncompleteArrayType());

llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
BuildTypeCoupledDecls(CountExpr, Decls);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -7344,7 +7344,7 @@ 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.BuildCountAttributedArrayType(InnerTy, NewCount);
}

TLB.push<CountAttributedTypeLoc>(Result);
Expand Down
45 changes: 0 additions & 45 deletions clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c

This file was deleted.

117 changes: 0 additions & 117 deletions clang/test/AST/attr-counted-by-struct-ptrs.c

This file was deleted.

26 changes: 0 additions & 26 deletions clang/test/Sema/attr-counted-by-late-parsed-off.c

This file was deleted.

254 changes: 0 additions & 254 deletions clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c

This file was deleted.

17 changes: 0 additions & 17 deletions clang/test/Sema/attr-counted-by-struct-ptrs-sizeless-types.c

This file was deleted.

224 changes: 0 additions & 224 deletions clang/test/Sema/attr-counted-by-struct-ptrs.c

This file was deleted.

11 changes: 0 additions & 11 deletions clang/test/Sema/attr-counted-by-vla-sizeless-types.c

This file was deleted.

196 changes: 0 additions & 196 deletions clang/test/Sema/attr-counted-by-vla.c

This file was deleted.

112 changes: 112 additions & 0 deletions clang/test/Sema/attr-counted-by.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s

#define __counted_by(f) __attribute__((counted_by(f)))

struct bar;

struct not_found {
int count;
struct bar *fam[] __counted_by(bork); // expected-error {{use of undeclared identifier 'bork'}}
};

struct no_found_count_not_in_substruct {
unsigned long flags;
unsigned char count; // expected-note {{'count' declared here}}
struct A {
int dummy;
int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}}
} a;
};

struct not_found_count_not_in_unnamed_substruct {
unsigned char count; // expected-note {{'count' declared here}}
struct {
int dummy;
int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}}
} a;
};

struct not_found_count_not_in_unnamed_substruct_2 {
struct {
unsigned char count; // expected-note {{'count' declared here}}
};
struct {
int dummy;
int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}}
} a;
};

struct not_found_count_in_other_unnamed_substruct {
struct {
unsigned char count;
} a1;

struct {
int dummy;
int array[] __counted_by(count); // expected-error {{use of undeclared identifier 'count'}}
};
};

struct not_found_count_in_other_substruct {
struct _a1 {
unsigned char count;
} a1;

struct {
int dummy;
int array[] __counted_by(count); // expected-error {{use of undeclared identifier 'count'}}
};
};

struct not_found_count_in_other_substruct_2 {
struct _a2 {
unsigned char count;
} a2;

int array[] __counted_by(count); // expected-error {{use of undeclared identifier 'count'}}
};

struct not_found_suggest {
int bork;
struct bar *fam[] __counted_by(blork); // expected-error {{use of undeclared identifier 'blork'}}
};

int global; // expected-note {{'global' declared here}}

struct found_outside_of_struct {
int bork;
struct bar *fam[] __counted_by(global); // expected-error {{field 'global' in 'counted_by' not inside structure}}
};

struct self_referrential {
int bork;
struct bar *self[] __counted_by(self); // expected-error {{use of undeclared identifier 'self'}}
};

struct non_int_count {
double dbl_count;
struct bar *fam[] __counted_by(dbl_count); // expected-error {{'counted_by' requires a non-boolean integer type argument}}
};

struct array_of_ints_count {
int integers[2];
struct bar *fam[] __counted_by(integers); // expected-error {{'counted_by' requires a non-boolean integer type argument}}
};

struct not_a_fam {
int count;
struct bar *non_fam __counted_by(count); // expected-error {{'counted_by' only applies to C99 flexible array members}}
};

struct not_a_c99_fam {
int count;
struct bar *non_c99_fam[0] __counted_by(count); // expected-error {{'counted_by' only applies to C99 flexible array members}}
};

struct annotated_with_anon_struct {
unsigned long flags;
struct {
unsigned char count;
int array[] __counted_by(crount); // expected-error {{use of undeclared identifier 'crount'}}
};
};