void updated this revision to Diff 553682.
void added a comment.
Modify sanitize scope to avoid the instruction creation.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D148381/new/
https://reviews.llvm.org/D148381
Files:
clang/include/clang/AST/ASTContext.h
clang/include/clang/AST/Decl.h
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/AST/ASTContext.cpp
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/Decl.cpp
clang/lib/AST/Expr.cpp
clang/lib/CodeGen/BackendUtil.cpp
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/CodeGen/CGExpr.cpp
clang/lib/CodeGen/CodeGenFunction.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/test/Misc/pragma-attribute-supported-attributes-list.test
clang/test/Misc/warning-flags.c
Index: clang/test/Misc/warning-flags.c
===================================================================
--- clang/test/Misc/warning-flags.c
+++ clang/test/Misc/warning-flags.c
@@ -41,6 +41,7 @@
CHECK-NEXT: warn_char_constant_too_large
CHECK-NEXT: warn_collection_expr_type
CHECK-NEXT: warn_conflicting_variadic
+CHECK-NEXT: warn_counted_by_placeholder
CHECK-NEXT: warn_delete_array_type
CHECK-NEXT: warn_double_const_requires_fp64
CHECK-NEXT: warn_drv_assuming_mfloat_abi_is
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -56,6 +56,7 @@
// CHECK-NEXT: ConsumableAutoCast (SubjectMatchRule_record)
// CHECK-NEXT: ConsumableSetOnRead (SubjectMatchRule_record)
// CHECK-NEXT: Convergent (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)
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -8380,6 +8380,21 @@
D->addAttr(ZeroCallUsedRegsAttr::Create(S.Context, Kind, AL));
}
+static void handleCountedByAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ // TODO: Probably needs more processing here. See Sema::AddAlignValueAttr.
+ 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 void handleFunctionReturnThunksAttr(Sema &S, Decl *D,
const ParsedAttr &AL) {
StringRef KindStr;
@@ -9326,6 +9341,10 @@
handleAvailableOnlyInDefaultEvalMethod(S, D, AL);
break;
+ case ParsedAttr::AT_CountedBy:
+ handleCountedByAttr(S, D, AL);
+ break;
+
// Microsoft attributes:
case ParsedAttr::AT_LayoutVersion:
handleLayoutVersion(S, D, AL);
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -17903,6 +17903,40 @@
"Broken injected-class-name");
}
+static const FieldDecl *FindFieldWithCountedByAttr(const RecordDecl *RD) {
+ for (const Decl *D : RD->decls()) {
+ if (const auto *FD = dyn_cast<FieldDecl>(D)) {
+ if (FD->hasAttr<CountedByAttr>())
+ return FD;
+ }
+
+ if (const auto *SubRD = dyn_cast<RecordDecl>(D)) {
+ return FindFieldWithCountedByAttr(SubRD);
+ }
+ }
+
+ return nullptr;
+}
+
+/// CheckCountedByAttr - Return an \p IdentifierInfo if the attribute field
+/// doesn't exist in the enclosing structure. Returns \p nullptr if the
+/// attribute field does exist.
+static const IdentifierInfo *CheckCountedByAttr(const RecordDecl *RD,
+ const FieldDecl *FD,
+ SourceRange &Loc) {
+ const CountedByAttr *ECA = FD->getAttr<CountedByAttr>();
+
+ auto It = llvm::find_if(RD->fields(), [&](const FieldDecl *Field) {
+ return Field->getName() == ECA->getCountedByField()->getName();
+ });
+ if (It == RD->field_end()) {
+ Loc = ECA->getCountedByFieldLoc();
+ return ECA->getCountedByField();
+ }
+
+ return nullptr;
+}
+
void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
SourceRange BraceRange) {
AdjustDeclIfTemplate(TagD);
@@ -17960,6 +17994,16 @@
[](const FieldDecl *FD) { return FD->isBitField(); }))
Diag(BraceRange.getBegin(), diag::warn_pragma_align_not_xl_compatible);
}
+
+ // Check the "counted_by" attribute to ensure that the count field exists in
+ // the struct.
+ if (const RecordDecl *RD = dyn_cast<RecordDecl>(Tag)) {
+ if (const FieldDecl *FD = FindFieldWithCountedByAttr(RD)) {
+ SourceRange SR;
+ if (const IdentifierInfo *Unknown = CheckCountedByAttr(RD, FD, SR))
+ Diag(SR.getBegin(), diag::warn_counted_by_placeholder) << Unknown << SR;
+ }
+ }
}
void Sema::ActOnObjCContainerFinishDefinition() {
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -528,8 +528,8 @@
/// Sanitizers enabled for this function.
SanitizerSet SanOpts;
- /// True if CodeGen currently emits code implementing sanitizer checks.
- bool IsSanitizerScope = false;
+ /// Non-zero if CodeGen currently emits code implementing sanitizer checks.
+ unsigned IsSanitizerScope = 0;
/// RAII object to set/unset CodeGenFunction::IsSanitizerScope.
class SanitizerScope {
@@ -3027,6 +3027,12 @@
void EmitBoundsCheck(const Expr *E, const Expr *Base, llvm::Value *Index,
QualType IndexType, bool Accessed);
+ /// Find the FieldDecl specified in a FAM's "counted_by" attribute. Returns
+ /// \p nullptr if either the attribute or the field doesn't exist.
+ FieldDecl *FindCountedByField(
+ const Expr *Base,
+ LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel);
+
llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
bool isInc, bool isPre);
ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV,
Index: clang/lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.cpp
+++ clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2554,7 +2554,7 @@
llvm::BasicBlock *BB,
llvm::BasicBlock::iterator InsertPt) const {
LoopStack.InsertHelper(I);
- if (IsSanitizerScope)
+ if (IsSanitizerScope != 0)
I->setNoSanitizeMetadata();
}
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -30,6 +30,7 @@
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Intrinsics.h"
@@ -925,16 +926,31 @@
if (CE->getCastKind() == CK_ArrayToPointerDecay &&
!CE->getSubExpr()->isFlexibleArrayMemberLike(CGF.getContext(),
StrictFlexArraysLevel)) {
+ CodeGenFunction::SanitizerScope SanScope(&CGF);
+
IndexedType = CE->getSubExpr()->getType();
const ArrayType *AT = IndexedType->castAsArrayTypeUnsafe();
if (const auto *CAT = dyn_cast<ConstantArrayType>(AT))
return CGF.Builder.getInt(CAT->getSize());
- else if (const auto *VAT = dyn_cast<VariableArrayType>(AT))
+
+ if (const auto *VAT = dyn_cast<VariableArrayType>(AT))
return CGF.getVLASize(VAT).NumElts;
// Ignore pass_object_size here. It's not applicable on decayed pointers.
}
+
+ if (FieldDecl *FD = CGF.FindCountedByField(Base, StrictFlexArraysLevel)) {
+ const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr());
+ IndexedType = Base->getType();
+ return CGF
+ .EmitAnyExprToTemp(MemberExpr::CreateImplicit(
+ CGF.getContext(), const_cast<Expr *>(ME->getBase()),
+ ME->isArrow(), FD, FD->getType(), VK_LValue, OK_Ordinary))
+ .getScalarVal();
+ }
}
+ CodeGenFunction::SanitizerScope SanScope(&CGF);
+
QualType EltTy{Base->getType()->getPointeeOrArrayElementType(), 0};
if (llvm::Value *POS = CGF.LoadPassedObjectSize(Base, EltTy)) {
IndexedType = Base->getType();
@@ -944,13 +960,52 @@
return nullptr;
}
+FieldDecl *CodeGenFunction::FindCountedByField(
+ const Expr *Base,
+ LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel) {
+ const ValueDecl *VD = nullptr;
+
+ Base = Base->IgnoreParenImpCasts();
+
+ if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
+ VD = dyn_cast<ValueDecl>(ME->getMemberDecl());
+ } else if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
+ // Pointing to the full structure.
+ VD = dyn_cast<ValueDecl>(DRE->getDecl());
+
+ QualType Ty = VD->getType();
+ if (Ty->isPointerType())
+ Ty = Ty->getPointeeType();
+
+ if (const auto *RD = Ty->getAsRecordDecl())
+ VD = RD->getLastField();
+ } else if (const auto *CE = dyn_cast<CastExpr>(Base)) {
+ if (const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr()))
+ VD = dyn_cast<ValueDecl>(ME->getMemberDecl());
+ }
+
+ const auto *FD = dyn_cast_if_present<FieldDecl>(VD);
+ if (!FD ||
+ !FD->isFlexibleArrayMemberLike(getContext(), StrictFlexArraysLevel, true))
+ return nullptr;
+
+ const auto *CBA = FD->getAttr<CountedByAttr>();
+ if (!CBA)
+ return nullptr;
+
+ StringRef FieldName = CBA->getCountedByField()->getName();
+ auto It =
+ llvm::find_if(FD->getParent()->fields(), [&](const FieldDecl *Field) {
+ return FieldName == Field->getName();
+ });
+ return It != FD->getParent()->field_end() ? *It : nullptr;
+}
+
void CodeGenFunction::EmitBoundsCheck(const Expr *E, const Expr *Base,
llvm::Value *Index, QualType IndexType,
bool Accessed) {
assert(SanOpts.has(SanitizerKind::ArrayBounds) &&
"should not be called unless adding bounds checks");
- SanitizerScope SanScope(this);
-
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
getLangOpts().getStrictFlexArraysLevel();
@@ -960,6 +1015,8 @@
if (!Bound)
return;
+ SanitizerScope SanScope(this);
+
bool IndexSigned = IndexType->isSignedIntegerOrEnumerationType();
llvm::Value *IndexVal = Builder.CreateIntCast(Index, SizeTy, IndexSigned);
llvm::Value *BoundVal = Builder.CreateIntCast(Bound, SizeTy, false);
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -853,6 +853,56 @@
}
}
+ if (IsDynamic) {
+ const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
+ getLangOpts().getStrictFlexArraysLevel();
+ const Expr *Base = E->IgnoreParenImpCasts();
+
+ if (FieldDecl *FD = FindCountedByField(Base, StrictFlexArraysLevel)) {
+ const auto *ME = dyn_cast<MemberExpr>(Base);
+ llvm::Value *ObjectSize = nullptr;
+
+ if (!ME) {
+ const auto *DRE = dyn_cast<DeclRefExpr>(Base);
+ ValueDecl *VD = nullptr;
+
+ ObjectSize = ConstantInt::get(
+ ResType,
+ getContext().getTypeSize(DRE->getType()->getPointeeType()) / 8,
+ true);
+
+ if (auto *RD = DRE->getType()->getPointeeType()->getAsRecordDecl())
+ for (FieldDecl *Field : RD->fields())
+ VD = Field;
+
+ Expr *ICE = ImplicitCastExpr::Create(
+ getContext(), DRE->getType(), CK_LValueToRValue,
+ const_cast<Expr *>(dyn_cast<Expr>(DRE)), nullptr, VK_PRValue,
+ FPOptionsOverride());
+ ME = MemberExpr::CreateImplicit(getContext(), ICE, true, VD,
+ VD->getType(), VK_LValue, OK_Ordinary);
+ }
+
+ // At this point, we know that \p ME is a flexible array member.
+ const auto *ArrayTy = dyn_cast<ArrayType>(ME->getType());
+ unsigned Size = getContext().getTypeSize(ArrayTy->getElementType());
+
+ llvm::Value *CountField =
+ EmitAnyExprToTemp(MemberExpr::CreateImplicit(
+ getContext(), const_cast<Expr *>(ME->getBase()),
+ ME->isArrow(), FD, FD->getType(), VK_LValue,
+ OK_Ordinary))
+ .getScalarVal();
+ llvm::Value *Mul = Builder.CreateMul(
+ CountField, llvm::ConstantInt::get(CountField->getType(), Size / 8));
+
+ if (ObjectSize)
+ return Builder.CreateAdd(ObjectSize, Mul);
+
+ return Mul;
+ }
+ }
+
// 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.
Index: clang/lib/CodeGen/BackendUtil.cpp
===================================================================
--- clang/lib/CodeGen/BackendUtil.cpp
+++ clang/lib/CodeGen/BackendUtil.cpp
@@ -981,7 +981,8 @@
// Register callbacks to schedule sanitizer passes at the appropriate part
// of the pipeline.
- if (LangOpts.Sanitize.has(SanitizerKind::LocalBounds))
+ if (LangOpts.Sanitize.has(SanitizerKind::LocalBounds) ||
+ LangOpts.Sanitize.has(SanitizerKind::ArrayBounds))
PB.registerScalarOptimizerLateEPCallback(
[](FunctionPassManager &FPM, OptimizationLevel Level) {
FPM.addPass(BoundsCheckingPass());
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ clang/lib/AST/Expr.cpp
@@ -205,85 +205,26 @@
}
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;
+ std::optional<bool> Res = Ctx.isCompatibleFlexibleArrayMemberLike(getType());
+ if (Res.has_value())
+ return *Res;
const Expr *E = IgnoreParens();
+ const ValueDecl *VD = 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))
+ VD = ME->getMemberDecl();
+ else if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
+ VD = DRE->getDecl();
else if (const auto *IRE = dyn_cast<ObjCIvarRefExpr>(E))
- return IRE->getDecl()->getNextIvar() == nullptr;
+ VD = IRE->getDecl();
- if (!ND)
- return false;
-
- // 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 VD ? VD->isFlexibleArrayMemberLike(Ctx, StrictFlexArraysLevel,
+ IgnoreTemplateOrMacroSubstitution)
+ : false;
}
const ValueDecl *
Index: clang/lib/AST/Decl.cpp
===================================================================
--- clang/lib/AST/Decl.cpp
+++ clang/lib/AST/Decl.cpp
@@ -5235,6 +5235,60 @@
return false;
}
+bool ValueDecl::isFlexibleArrayMemberLike(
+ 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.
+ std::optional<bool> Res = Ctx.isCompatibleFlexibleArrayMemberLike(getType());
+ if (Res.has_value())
+ return *Res;
+
+ if (const auto *OID = dyn_cast<ObjCIvarDecl>(this))
+ return OID->getNextIvar() == nullptr;
+
+ const auto *FD = dyn_cast<FieldDecl>(this);
+ if (!FD)
+ return false;
+
+ if (const auto *CAT = Ctx.getAsConstantArrayType(getType())) {
+ // GCC treats an array memeber of a union as an FAM if the size is one or
+ // zero.
+ 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 (auto CTL = TL.getAs<ConstantArrayTypeLoc>()) {
+ const Expr *SizeExpr = 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();
+}
+
void ImplicitParamDecl::anchor() {}
ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC,
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -8891,6 +8891,10 @@
public:
AttrImporter(ASTImporter &I) : Importer(I), NImporter(I) {}
+ // Useful for accessing the imported attribute.
+ template <typename T> T *getAttrAs() { return cast<T>(ToAttr); }
+ template <typename T> const T *getAttrAs() const { return cast<T>(ToAttr); }
+
// Create an "importer" for an attribute parameter.
// Result of the 'value()' of that object is to be passed to the function
// 'importAttr', in the order that is expected by the attribute class.
@@ -9097,6 +9101,15 @@
From->args_size());
break;
}
+ case attr::CountedBy: {
+ AI.cloneAttr(FromAttr);
+ const auto *ECA = cast<CountedByAttr>(FromAttr);
+ Expected<SourceRange> SR = Import(ECA->getCountedByFieldLoc()).get();
+ if (!SR)
+ return SR.takeError();
+ AI.getAttrAs<CountedByAttr>()->setCountedByFieldLoc(SR.get());
+ break;
+ }
default: {
// The default branch works for attributes that have no arguments to import.
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -12016,6 +12016,35 @@
return ABI->isNearlyEmpty(RD);
}
+/// For compatibility with existing code, we treat arrays of length 0 or 1 as
+/// flexible array members.
+std::optional<bool>
+ASTContext::isCompatibleFlexibleArrayMemberLike(QualType Ty) const {
+ if (const auto *CAT = getAsConstantArrayType(Ty)) {
+ using FAMKind = LangOptions::StrictFlexArraysLevelKind;
+
+ llvm::APInt Size = CAT->getSize();
+ FAMKind StrictFlexArraysLevel = getLangOpts().getStrictFlexArraysLevel();
+
+ 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 (!getAsIncompleteArrayType(Ty)) {
+ return false;
+ }
+
+ return {};
+}
+
VTableContextBase *ASTContext::getVTableContext() {
if (!VTContext.get()) {
auto ABI = Target->getCXXABI();
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3419,6 +3419,9 @@
def err_alignment_dependent_typedef_name : Error<
"requested alignment is dependent but declaration is not dependent">;
+def warn_counted_by_placeholder : Warning<
+ "counted_by field %0 not found">;
+
def warn_alignment_builtin_useless : Warning<
"%select{aligning a value|the result of checking whether a value is aligned}0"
" to 1 byte is %select{a no-op|always true}0">, InGroup<TautologicalCompare>;
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -7189,3 +7189,47 @@
(if any) of these ``cleanup`` callback functions.
}];
}
+
+def CountedByDocs : Documentation {
+ let Category = DocCatField;
+ let Content = [{
+Clang supports the ``counted_by`` attribute for flexible array members. The
+argument for the ``counted_by`` attribute is the name of a field member in
+the same structure holding the count of elements in the flexible array.
+
+For example:
+
+.. code-block:: c
+
+ struct bar;
+
+ struct foo {
+ size_t num_fam_elements;
+ /* ... */
+ struct bar *fam[] __attribute__((counted_by(num_fam_elements)));
+ };
+
+ struct foo *foo_alloc(size_t num_elements) {
+ struct foo *f;
+
+ f = malloc(sizeof(struct foo) + num_elements * sizeof(struct bar *));
+ f->num_fam_elements = num_elements;
+ return f;
+ }
+
+This attribute is used with the ``-fsantize=array-bounds`` flag to trap if a
+flexible array access is outside of the number of elements.
+
+.. code-block:: c
+
+ void mux(struct foo *);
+
+ struct bar *baz(void) {
+ struct foo *f = foo_alloc(128);
+
+ mux(f); /* Fills in fam element. */
+
+ return f->fam[256]; /* Trap. */
+ }
+ }];
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -4233,3 +4233,17 @@
let Documentation = [Undocumented];
}
+def CountedBy : InheritableAttr {
+ let Spellings = [Clang<"counted_by">];
+ let Subjects = SubjectList<[Field]>;
+ let Args = [IdentifierArgument<"CountedByField">];
+ let Documentation = [CountedByDocs];
+ let LangOpts = [COnly];
+ code AdditionalMembers = [{
+ private:
+ SourceRange countedByFieldLoc;
+ public:
+ SourceRange getCountedByFieldLoc(void) const { return countedByFieldLoc; }
+ void setCountedByFieldLoc(SourceRange Loc) { countedByFieldLoc = Loc; }
+ }];
+}
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -26,6 +26,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Linkage.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/PartialDiagnostic.h"
@@ -730,6 +731,12 @@
return const_cast<ValueDecl *>(this)->getPotentiallyDecomposedVarDecl();
}
+ /// Whether it resembles a flexible array member.
+ bool isFlexibleArrayMemberLike(
+ ASTContext &Context,
+ LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
+ bool IgnoreTemplateOrMacroSubstitution) const;
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K >= firstValue && K <= lastValue; }
@@ -4268,6 +4275,19 @@
return field_begin() == field_end();
}
+ FieldDecl *getLastField() {
+ FieldDecl *FD = nullptr;
+ for (FieldDecl *Field : fields())
+ FD = Field;
+ return FD;
+ }
+ const FieldDecl *getLastField() const {
+ const FieldDecl *FD = nullptr;
+ for (const FieldDecl *Field : fields())
+ FD = Field;
+ return FD;
+ }
+
/// Note that the definition of this type is now complete.
virtual void completeDefinition();
Index: clang/include/clang/AST/ASTContext.h
===================================================================
--- clang/include/clang/AST/ASTContext.h
+++ clang/include/clang/AST/ASTContext.h
@@ -2485,6 +2485,10 @@
bool isNearlyEmpty(const CXXRecordDecl *RD) const;
+ /// For compatibility with existing code, we treat arrays of length 0 or
+ /// 1 as flexible array members.
+ std::optional<bool> isCompatibleFlexibleArrayMemberLike(QualType Ty) const;
+
VTableContextBase *getVTableContext();
/// If \p T is null pointer, assume the target in ASTContext.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits