83 changes: 10 additions & 73 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,85 +205,22 @@ bool Expr::isKnownToHaveBooleanValue(bool Semantic) const {
}

bool Expr::isFlexibleArrayMemberLike(
ASTContext &Context,
ASTContext &Ctx,
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
bool IgnoreTemplateOrMacroSubstitution) const {

// For compatibility with existing code, we treat arrays of length 0 or
// 1 as flexible array members.
const auto *CAT = Context.getAsConstantArrayType(getType());
if (CAT) {
llvm::APInt Size = CAT->getSize();

using FAMKind = LangOptions::StrictFlexArraysLevelKind;

if (StrictFlexArraysLevel == FAMKind::IncompleteOnly)
return false;

// GCC extension, only allowed to represent a FAM.
if (Size == 0)
return true;

if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete && Size.uge(1))
return false;

if (StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete && Size.uge(2))
return false;
} else if (!Context.getAsIncompleteArrayType(getType()))
return false;

const Expr *E = IgnoreParens();
const Decl *D = nullptr;

const NamedDecl *ND = nullptr;
if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
ND = DRE->getDecl();
else if (const auto *ME = dyn_cast<MemberExpr>(E))
ND = ME->getMemberDecl();
if (const auto *ME = dyn_cast<MemberExpr>(E))
D = ME->getMemberDecl();
else if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
D = DRE->getDecl();
else if (const auto *IRE = dyn_cast<ObjCIvarRefExpr>(E))
return IRE->getDecl()->getNextIvar() == nullptr;

if (!ND)
return false;
D = IRE->getDecl();

// A flexible array member must be the last member in the class.
// FIXME: If the base type of the member expr is not FD->getParent(),
// this should not be treated as a flexible array member access.
if (const auto *FD = dyn_cast<FieldDecl>(ND)) {
// GCC treats an array memeber of a union as an FAM if the size is one or
// zero.
if (CAT) {
llvm::APInt Size = CAT->getSize();
if (FD->getParent()->isUnion() && (Size.isZero() || Size.isOne()))
return true;
}

// Don't consider sizes resulting from macro expansions or template argument
// substitution to form C89 tail-padded arrays.
if (IgnoreTemplateOrMacroSubstitution) {
TypeSourceInfo *TInfo = FD->getTypeSourceInfo();
while (TInfo) {
TypeLoc TL = TInfo->getTypeLoc();
// Look through typedefs.
if (TypedefTypeLoc TTL = TL.getAsAdjusted<TypedefTypeLoc>()) {
const TypedefNameDecl *TDL = TTL.getTypedefNameDecl();
TInfo = TDL->getTypeSourceInfo();
continue;
}
if (ConstantArrayTypeLoc CTL = TL.getAs<ConstantArrayTypeLoc>()) {
const Expr *SizeExpr = dyn_cast<IntegerLiteral>(CTL.getSizeExpr());
if (!SizeExpr || SizeExpr->getExprLoc().isMacroID())
return false;
}
break;
}
}

RecordDecl::field_iterator FI(
DeclContext::decl_iterator(const_cast<FieldDecl *>(FD)));
return ++FI == FD->getParent()->field_end();
}

return false;
return Decl::isFlexibleArrayMemberLike(Ctx, D, E->getType(),
StrictFlexArraysLevel,
IgnoreTemplateOrMacroSubstitution);
}

const ValueDecl *
Expand Down
240 changes: 240 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/OSLog.h"
#include "clang/AST/OperationKinds.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
Expand Down Expand Up @@ -818,6 +819,238 @@ CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
}

const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberField(
ASTContext &Ctx, const RecordDecl *RD, StringRef Name, uint64_t &Offset) {
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
getLangOpts().getStrictFlexArraysLevel();
unsigned FieldNo = 0;
bool IsUnion = RD->isUnion();

for (const Decl *D : RD->decls()) {
if (const auto *Field = dyn_cast<FieldDecl>(D);
Field && (Name.empty() || Field->getNameAsString() == Name) &&
Decl::isFlexibleArrayMemberLike(
Ctx, Field, Field->getType(), StrictFlexArraysLevel,
/*IgnoreTemplateOrMacroSubstitution=*/true)) {
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
Offset += Layout.getFieldOffset(FieldNo);
return Field;
}

if (const auto *Record = dyn_cast<RecordDecl>(D))
if (const FieldDecl *Field =
FindFlexibleArrayMemberField(Ctx, Record, Name, Offset)) {
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
Offset += Layout.getFieldOffset(FieldNo);
return Field;
}

if (!IsUnion && isa<FieldDecl>(D))
++FieldNo;
}

return nullptr;
}

static unsigned CountCountedByAttrs(const RecordDecl *RD) {
unsigned Num = 0;

for (const Decl *D : RD->decls()) {
if (const auto *FD = dyn_cast<FieldDecl>(D);
FD && FD->hasAttr<CountedByAttr>()) {
return ++Num;
}

if (const auto *Rec = dyn_cast<RecordDecl>(D))
Num += CountCountedByAttrs(Rec);
}

return Num;
}

llvm::Value *
CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
llvm::IntegerType *ResType) {
// The code generated here calculates the size of a struct with a flexible
// array member that uses the counted_by attribute. There are two instances
// we handle:
//
// struct s {
// unsigned long flags;
// int count;
// int array[] __attribute__((counted_by(count)));
// }
//
// 1) bdos of the flexible array itself:
//
// __builtin_dynamic_object_size(p->array, 1) ==
// p->count * sizeof(*p->array)
//
// 2) bdos of a pointer into the flexible array:
//
// __builtin_dynamic_object_size(&p->array[42], 1) ==
// (p->count - 42) * sizeof(*p->array)
//
// 2) bdos of the whole struct, including the flexible array:
//
// __builtin_dynamic_object_size(p, 1) ==
// max(sizeof(struct s),
// offsetof(struct s, array) + p->count * sizeof(*p->array))
//
ASTContext &Ctx = getContext();
const Expr *Base = E->IgnoreParenImpCasts();
const Expr *Idx = nullptr;

if (const auto *UO = dyn_cast<UnaryOperator>(Base);
UO && UO->getOpcode() == UO_AddrOf) {
Expr *SubExpr = UO->getSubExpr()->IgnoreParenImpCasts();
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(SubExpr)) {
Base = ASE->getBase()->IgnoreParenImpCasts();
Idx = ASE->getIdx()->IgnoreParenImpCasts();

if (const auto *IL = dyn_cast<IntegerLiteral>(Idx)) {
int64_t Val = IL->getValue().getSExtValue();
if (Val < 0)
return getDefaultBuiltinObjectSizeResult(Type, ResType);

if (Val == 0)
// The index is 0, so we don't need to take it into account.
Idx = nullptr;
}
} else {
// Potential pointer to another element in the struct.
Base = SubExpr;
}
}

// Get the flexible array member Decl.
const RecordDecl *OuterRD = nullptr;
std::string FAMName;
if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
// Check if \p Base is referencing the FAM itself.
const ValueDecl *VD = ME->getMemberDecl();
OuterRD = VD->getDeclContext()->getOuterLexicalRecordContext();
FAMName = VD->getNameAsString();
} else if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
// Check if we're pointing to the whole struct.
QualType Ty = DRE->getDecl()->getType();
if (Ty->isPointerType())
Ty = Ty->getPointeeType();
OuterRD = Ty->getAsRecordDecl();

// If we have a situation like this:
//
// struct union_of_fams {
// int flags;
// union {
// signed char normal_field;
// struct {
// int count1;
// int arr1[] __counted_by(count1);
// };
// struct {
// signed char count2;
// int arr2[] __counted_by(count2);
// };
// };
// };
//
// We don't konw which 'count' to use in this scenario:
//
// size_t get_size(struct union_of_fams *p) {
// return __builtin_dynamic_object_size(p, 1);
// }
//
// Instead of calculating a wrong number, we give up.
if (OuterRD && CountCountedByAttrs(OuterRD) > 1)
return nullptr;
}

if (!OuterRD)
return nullptr;

uint64_t Offset = 0;
const FieldDecl *FAMDecl =
FindFlexibleArrayMemberField(Ctx, OuterRD, FAMName, Offset);
Offset = Ctx.toCharUnitsFromBits(Offset).getQuantity();

if (!FAMDecl || !FAMDecl->hasAttr<CountedByAttr>())
// No flexible array member found or it doesn't have the "counted_by"
// attribute.
return nullptr;

const FieldDecl *CountedByFD = FindCountedByField(FAMDecl);
if (!CountedByFD)
// Can't find the field referenced by the "counted_by" attribute.
return nullptr;

// Build a load of the counted_by field.
bool IsSigned = CountedByFD->getType()->isSignedIntegerType();
Value *CountedByInst = EmitCountedByFieldExpr(Base, FAMDecl, CountedByFD);
if (!CountedByInst)
return getDefaultBuiltinObjectSizeResult(Type, ResType);

CountedByInst = Builder.CreateIntCast(CountedByInst, ResType, IsSigned);

// Build a load of the index and subtract it from the count.
Value *IdxInst = nullptr;
if (Idx) {
if (Idx->HasSideEffects(getContext()))
// We can't have side-effects.
return getDefaultBuiltinObjectSizeResult(Type, ResType);

bool IdxSigned = Idx->getType()->isSignedIntegerType();
IdxInst = EmitAnyExprToTemp(Idx).getScalarVal();
IdxInst = Builder.CreateIntCast(IdxInst, ResType, IdxSigned);

// We go ahead with the calculation here. If the index turns out to be
// negative, we'll catch it at the end.
CountedByInst =
Builder.CreateSub(CountedByInst, IdxInst, "", !IsSigned, IsSigned);
}

// Calculate how large the flexible array member is in bytes.
const ArrayType *ArrayTy = Ctx.getAsArrayType(FAMDecl->getType());
CharUnits Size = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
llvm::Constant *ElemSize =
llvm::ConstantInt::get(ResType, Size.getQuantity(), IsSigned);
Value *FAMSize =
Builder.CreateMul(CountedByInst, ElemSize, "", !IsSigned, IsSigned);
FAMSize = Builder.CreateIntCast(FAMSize, ResType, IsSigned);
Value *Res = FAMSize;

if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
// The whole struct is specificed in the __bdos.
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(OuterRD);

// Get the offset of the FAM.
llvm::Constant *FAMOffset = ConstantInt::get(ResType, Offset, IsSigned);
Value *OffsetAndFAMSize =
Builder.CreateAdd(FAMOffset, Res, "", !IsSigned, IsSigned);

// Get the full size of the struct.
llvm::Constant *SizeofStruct =
ConstantInt::get(ResType, Layout.getSize().getQuantity(), IsSigned);

// max(sizeof(struct s),
// offsetof(struct s, array) + p->count * sizeof(*p->array))
Res = IsSigned
? Builder.CreateBinaryIntrinsic(llvm::Intrinsic::smax,
OffsetAndFAMSize, SizeofStruct)
: Builder.CreateBinaryIntrinsic(llvm::Intrinsic::umax,
OffsetAndFAMSize, SizeofStruct);
}

// A negative \p IdxInst or \p CountedByInst means that the index lands
// outside of the flexible array member. If that's the case, we want to
// return 0.
Value *Cmp = Builder.CreateIsNotNeg(CountedByInst);
if (IdxInst)
Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(IdxInst), Cmp);

return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
}

/// Returns a Value corresponding to the size of the given expression.
/// This Value may be either of the following:
/// - A llvm::Argument (if E is a param with the pass_object_size attribute on
Expand Down Expand Up @@ -850,6 +1083,13 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
}
}

if (IsDynamic) {
// Emit special code for a flexible array member with the "counted_by"
// attribute.
if (Value *V = emitFlexibleArrayMemberSize(E, Type, ResType))
return V;
}

// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
// evaluate E for side-effects. In either case, we shouldn't lower to
// @llvm.objectsize.
Expand Down
340 changes: 334 additions & 6 deletions clang/lib/CodeGen/CGExpr.cpp

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3073,6 +3073,25 @@ class CodeGenFunction : public CodeGenTypeCache {
/// this expression is used as an lvalue, for instance in "&Arr[Idx]".
void EmitBoundsCheck(const Expr *E, const Expr *Base, llvm::Value *Index,
QualType IndexType, bool Accessed);
void EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound,
llvm::Value *Index, QualType IndexType,
QualType IndexedType, bool Accessed);

// Find a struct's flexible array member. It may be embedded inside multiple
// sub-structs, but must still be the last field.
const FieldDecl *FindFlexibleArrayMemberField(ASTContext &Ctx,
const RecordDecl *RD,
StringRef Name,
uint64_t &Offset);

/// Find the FieldDecl specified in a FAM's "counted_by" attribute. Returns
/// \p nullptr if either the attribute or the field doesn't exist.
const FieldDecl *FindCountedByField(const FieldDecl *FD);

/// Build an expression accessing the "counted_by" field.
llvm::Value *EmitCountedByFieldExpr(const Expr *Base,
const FieldDecl *FAMDecl,
const FieldDecl *CountDecl);

llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
bool isInc, bool isPre);
Expand Down Expand Up @@ -4873,6 +4892,9 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Value *EmittedE,
bool IsDynamic);

llvm::Value *emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
llvm::IntegerType *ResType);

void emitZeroOrPatternForAutoVarInit(QualType type, const VarDecl &D,
Address Loc);

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2315,6 +2315,12 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) {
}
ShadowingDecls.erase(ShadowI);
}

if (!getLangOpts().CPlusPlus && S->isClassScope()) {
if (auto *FD = dyn_cast<FieldDecl>(TmpD);
FD && FD->hasAttr<CountedByAttr>())
CheckCountedByAttr(S, FD);
}
}

llvm::sort(DeclDiags,
Expand Down
133 changes: 133 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8460,6 +8460,135 @@ static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(ZeroCallUsedRegsAttr::Create(S.Context, Kind, AL));
}

static void handleCountedByAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (!AL.isArgIdent(0)) {
S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
<< AL << AANT_ArgumentIdentifier;
return;
}

IdentifierLoc *IL = AL.getArgAsIdent(0);
CountedByAttr *CBA =
::new (S.Context) CountedByAttr(S.Context, AL, IL->Ident);
CBA->setCountedByFieldLoc(IL->Loc);
D->addAttr(CBA);
}

static const FieldDecl *
FindFieldInTopLevelOrAnonymousStruct(const RecordDecl *RD,
const IdentifierInfo *FieldName) {
for (const Decl *D : RD->decls()) {
if (const auto *FD = dyn_cast<FieldDecl>(D))
if (FD->getName() == FieldName->getName())
return FD;

if (const auto *R = dyn_cast<RecordDecl>(D))
if (const FieldDecl *FD =
FindFieldInTopLevelOrAnonymousStruct(R, FieldName))
return FD;
}

return nullptr;
}

bool Sema::CheckCountedByAttr(Scope *S, const FieldDecl *FD) {
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
LangOptions::StrictFlexArraysLevelKind::IncompleteOnly;
if (!Decl::isFlexibleArrayMemberLike(Context, FD, FD->getType(),
StrictFlexArraysLevel, true)) {
// The "counted_by" attribute must be on a flexible array member.
SourceRange SR = FD->getLocation();
Diag(SR.getBegin(), diag::err_counted_by_attr_not_on_flexible_array_member)
<< SR;
return true;
}

const auto *CBA = FD->getAttr<CountedByAttr>();
const IdentifierInfo *FieldName = CBA->getCountedByField();

auto GetNonAnonStructOrUnion = [](const RecordDecl *RD) {
while (RD && !RD->getDeclName())
if (const auto *R = dyn_cast<RecordDecl>(RD->getDeclContext()))
RD = R;
else
break;

return RD;
};

const RecordDecl *EnclosingRD = GetNonAnonStructOrUnion(FD->getParent());
const FieldDecl *CountFD =
FindFieldInTopLevelOrAnonymousStruct(EnclosingRD, FieldName);

if (!CountFD) {
DeclarationNameInfo NameInfo(FieldName,
CBA->getCountedByFieldLoc().getBegin());
LookupResult MemResult(*this, NameInfo, Sema::LookupMemberName);
LookupName(MemResult, S);

if (!MemResult.empty()) {
SourceRange SR = CBA->getCountedByFieldLoc();
Diag(SR.getBegin(), diag::err_flexible_array_count_not_in_same_struct)
<< CBA->getCountedByField() << SR;

if (auto *ND = MemResult.getAsSingle<NamedDecl>()) {
SR = ND->getLocation();
Diag(SR.getBegin(), diag::note_flexible_array_counted_by_attr_field)
<< ND << SR;
}

return true;
} else {
// The "counted_by" field needs to exist in the struct.
LookupResult OrdResult(*this, NameInfo, Sema::LookupOrdinaryName);
LookupName(OrdResult, S);

if (!OrdResult.empty()) {
SourceRange SR = FD->getLocation();
Diag(SR.getBegin(), diag::err_counted_by_must_be_in_structure)
<< FieldName << SR;

if (auto *ND = OrdResult.getAsSingle<NamedDecl>()) {
SR = ND->getLocation();
Diag(SR.getBegin(), diag::note_flexible_array_counted_by_attr_field)
<< ND << SR;
}

return true;
}
}

CXXScopeSpec SS;
DeclFilterCCC<FieldDecl> Filter(FieldName);
return DiagnoseEmptyLookup(S, SS, MemResult, Filter, nullptr, std::nullopt,
const_cast<DeclContext *>(FD->getDeclContext()));
}

if (CountFD->hasAttr<CountedByAttr>()) {
// The "counted_by" field can't point to the flexible array member.
SourceRange SR = CBA->getCountedByFieldLoc();
Diag(SR.getBegin(), diag::err_counted_by_attr_refers_to_flexible_array)
<< CBA->getCountedByField() << SR;
return true;
}

if (!CountFD->getType()->isIntegerType() ||
CountFD->getType()->isBooleanType()) {
// The "counted_by" field must have an integer type.
SourceRange SR = CBA->getCountedByFieldLoc();
Diag(SR.getBegin(),
diag::err_flexible_array_counted_by_attr_field_not_integer)
<< CBA->getCountedByField() << SR;

SR = CountFD->getLocation();
Diag(SR.getBegin(), diag::note_flexible_array_counted_by_attr_field)
<< CountFD << SR;
return true;
}

return false;
}

static void handleFunctionReturnThunksAttr(Sema &S, Decl *D,
const ParsedAttr &AL) {
StringRef KindStr;
Expand Down Expand Up @@ -9420,6 +9549,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
handleAvailableOnlyInDefaultEvalMethod(S, D, AL);
break;

case ParsedAttr::AT_CountedBy:
handleCountedByAttr(S, D, AL);
break;

// Microsoft attributes:
case ParsedAttr::AT_LayoutVersion:
handleLayoutVersion(S, D, AL);
Expand Down
16 changes: 9 additions & 7 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2469,7 +2469,8 @@ bool Sema::DiagnoseDependentMemberLookup(const LookupResult &R) {
bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
CorrectionCandidateCallback &CCC,
TemplateArgumentListInfo *ExplicitTemplateArgs,
ArrayRef<Expr *> Args, TypoExpr **Out) {
ArrayRef<Expr *> Args, DeclContext *LookupCtx,
TypoExpr **Out) {
DeclarationName Name = R.getLookupName();

unsigned diagnostic = diag::err_undeclared_var_use;
Expand All @@ -2485,7 +2486,8 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
// unqualified lookup. This is useful when (for example) the
// original lookup would not have found something because it was a
// dependent name.
DeclContext *DC = SS.isEmpty() ? CurContext : nullptr;
DeclContext *DC =
LookupCtx ? LookupCtx : (SS.isEmpty() ? CurContext : nullptr);
while (DC) {
if (isa<CXXRecordDecl>(DC)) {
LookupQualifiedName(R, DC);
Expand Down Expand Up @@ -2528,12 +2530,12 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
emitEmptyLookupTypoDiagnostic(TC, *this, SS, Name, TypoLoc, Args,
diagnostic, diagnostic_suggest);
},
nullptr, CTK_ErrorRecovery);
nullptr, CTK_ErrorRecovery, LookupCtx);
if (*Out)
return true;
} else if (S &&
(Corrected = CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(),
S, &SS, CCC, CTK_ErrorRecovery))) {
} else if (S && (Corrected =
CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(), S,
&SS, CCC, CTK_ErrorRecovery, LookupCtx))) {
std::string CorrectedStr(Corrected.getAsString(getLangOpts()));
bool DroppedSpecifier =
Corrected.WillReplaceSpecifier() && Name.getAsString() == CorrectedStr;
Expand Down Expand Up @@ -2823,7 +2825,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
// a template name, but we happen to have always already looked up the name
// before we get here if it must be a template name.
if (DiagnoseEmptyLookup(S, SS, R, CCC ? *CCC : DefaultValidator, nullptr,
std::nullopt, &TE)) {
std::nullopt, nullptr, &TE)) {
if (TE && KeywordReplacement) {
auto &State = getTypoExprState(TE);
auto BestTC = State.Consumer->getNextCorrection();
Expand Down
1,828 changes: 1,828 additions & 0 deletions clang/test/CodeGen/attr-counted-by.c

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion clang/test/CodeGen/bounds-checking.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ int f7(union U *u, int i) {
return u->c[i];
}


char B[10];
char B2[10];
// CHECK-LABEL: @f8
Expand All @@ -82,3 +81,12 @@ void f8(int i, int k) {
// NOOPTARRAY: call void @llvm.ubsantrap(i8 4)
B2[k] = '\0';
}

// See commit 9a954c6 that caused a SEGFAULT in this code.
struct S {
__builtin_va_list ap;
} *s;
// CHECK-LABEL: @f9
struct S *f9(int i) {
return &s[i];
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
// CHECK-NEXT: CoroOnlyDestroyWhenComplete (SubjectMatchRule_record)
// CHECK-NEXT: CoroReturnType (SubjectMatchRule_record)
// CHECK-NEXT: CoroWrapper (SubjectMatchRule_function)
// CHECK-NEXT: CountedBy (SubjectMatchRule_field)
// CHECK-NEXT: DLLExport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
// CHECK-NEXT: DLLImport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
// CHECK-NEXT: Destructor (SubjectMatchRule_function)
Expand Down
64 changes: 64 additions & 0 deletions clang/test/Sema/attr-counted-by.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 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 {{field '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_suggest {
int bork; // expected-note {{'bork' declared here}}
struct bar *fam[] __counted_by(blork); // expected-error {{use of undeclared identifier 'blork'; did you mean 'bork'?}}
};

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 {{'counted_by' cannot refer to the flexible array 'self'}}
};

struct non_int_count {
double dbl_count; // expected-note {{field 'dbl_count' declared here}}
struct bar *fam[] __counted_by(dbl_count); // expected-error {{field 'dbl_count' in 'counted_by' must be a non-boolean integer type}}
};

struct array_of_ints_count {
int integers[2]; // expected-note {{field 'integers' declared here}}
struct bar *fam[] __counted_by(integers); // expected-error {{field 'integers' in 'counted_by' must be a non-boolean integer type}}
};

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; // expected-note {{'count' declared here}}
int array[] __counted_by(crount); // expected-error {{use of undeclared identifier 'crount'; did you mean 'count'?}}
};
};
5 changes: 1 addition & 4 deletions llvm/tools/llvm-exegesis/lib/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@ extern cl::OptionCategory BenchmarkOptions;
extern cl::OptionCategory AnalysisOptions;

enum ValidationEvent {
L1DCacheLoadMiss,
InstructionRetired,
DataTLBLoadMiss,
DataTLBStoreMiss
InstructionRetired
};

struct PfmCountersInfo {
Expand Down