[clang] [Clang] Add -Wcounted-by-addrof to warn when &fam discards __counted_by (PR #206760)
David Meng via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 19 12:59:59 PDT 2026
https://github.com/davidmenggx updated https://github.com/llvm/llvm-project/pull/206760
>From 8e9caafb90100a74060177bd1b12756102864912 Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Tue, 30 Jun 2026 09:01:01 -0700
Subject: [PATCH 1/2] [Clang] Add -Wcounted-by-addrof to warn when &fam
discards __counted_by
Taking the address of a whole `__counted_by` flexible array member
(`&p->fam`) bypasses `emitCountedBySize()` in CodeGen and falls through to
layout-derived `llvm.objectsize`, silently discarding the annotation.
Under `-fbounds-safety` this is already an error. This adds a warning
`-Wcounted-by-addrof` (default off) for non-bounds-safety mode.
The warning fires when unary `&` is applied to the FAM as a whole.
`&fam[idx]`, the decayed `fam`, and `__counted_by` pointer fields are excluded.
A fix-it is offered only when the object's allocation is not statically
known (pointer bases, opaque returns, etc.), where the count-derived
bound is the useful answer.
Closes #206536
---
clang/docs/ReleaseNotes.md | 5 ++
clang/include/clang/Basic/DiagnosticGroups.td | 1 +
.../clang/Basic/DiagnosticSemaKinds.td | 6 ++
clang/lib/Sema/SemaExpr.cpp | 66 ++++++++++++++++
clang/test/Sema/warn-counted-by-addrof.c | 77 +++++++++++++++++++
5 files changed, 155 insertions(+)
create mode 100644 clang/test/Sema/warn-counted-by-addrof.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 9301745b9628e..1a246419b7b0a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -290,6 +290,11 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
against or converted to a null pointer, the same as a bare function name.
(#GH46362)
+- Added `-Wcounted-by-addrof` (default off) warning when unary `&` is applied to a `__counted_by`
+ flexible array member as a whole (e.g. `&p->fam`). This form bypasses the count when lowering
+ `__builtin_dynamic_object_size` and falls back to the object's static layout, silently discarding
+ the annotation. A fix-it removes the `&` when the object's allocation is not statically known,
+ where the count-derived bound is the useful answer.
### Improvements to Clang's time-trace
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 79583534b9bbd..bb3c2a8f57436 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1869,6 +1869,7 @@ def NoDeref : DiagGroup<"noderef">;
// -fbounds-safety and bounds annotation related warnings
def BoundsSafetyCountedByEltTyUnknownSize :
DiagGroup<"bounds-safety-counted-by-elt-type-unknown-size">;
+def CountedByAddrof : DiagGroup<"counted-by-addrof">;
// A group for cross translation unit static analysis related warnings.
def CrossTU : DiagGroup<"ctu">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 89e2f956971b3..b4cb0ff4e274b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7230,6 +7230,12 @@ def note_counted_by_consider_using_sized_by : Note<
def warn_counted_by_attr_elt_type_unknown_size :
Warning<err_counted_by_attr_pointee_unknown_size.Summary>,
InGroup<BoundsSafetyCountedByEltTyUnknownSize>;
+def warn_counted_by_addrof_discards_count : Warning<
+ "taking the address of flexible array member %0 discards the "
+ "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' bound; "
+ "'__builtin_dynamic_object_size' on the resulting pointer uses the static "
+ "layout of the object, not the annotated count">,
+ InGroup<CountedByAddrof>, DefaultIgnore;
// __builtin_counted_by_ref diagnostics:
def err_builtin_counted_by_ref_invalid_arg
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b844670543a55..d6173a75fc24e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -14970,6 +14970,68 @@ bool Sema::CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc,
<< FixItHint::CreateInsertion(DRE->getSourceRange().getBegin(), Qual);
}
+/// Returns true if the object holding the FAM reached by \p ME has a
+/// statically-known allocation (automatic/static/global). There the layout
+/// answer __bdos already gives is the real bound, so we offer no fix-it. Only
+/// when the object is reached through a pointer does the count carry the useful
+/// bound and the "drop the '&'" fix-it apply.
+static bool flexibleArrayMemberHasFixedStorage(const MemberExpr *ME) {
+ const Expr *E = ME;
+ while (true) {
+ if (const auto *M = dyn_cast<MemberExpr>(E)) {
+ if (M->isArrow())
+ return false; // Reached through a pointer.
+ E = M->getBase()->IgnoreParenImpCasts();
+ continue;
+ }
+ if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
+ const Expr *Base = ASE->getBase()->IgnoreParenImpCasts();
+ // Array subscript keeps the storage, pointer subscript escapes it.
+ if (Base->getType()->isPointerType())
+ return false;
+ E = Base;
+ continue;
+ }
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+ const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
+ // Locals, params, statics and globals all have a fixed allocation.
+ return VD && (VD->hasLocalStorage() || VD->hasGlobalStorage());
+ }
+ return false; // Call temporaries, opaque returns, etc.
+ }
+}
+
+/// -Wcounted-by-addrof: warn when unary '&' is applied to a whole
+/// '__counted_by' flexible array member. CodeGen bypasses emitCountedBySize()
+/// for this shape and falls back to layout-derived llvm.objectsize, discarding
+/// the count.
+static void DiagnoseCountedByAddrOf(Sema &S, SourceLocation OpLoc, Expr *Op) {
+ if (S.getLangOpts().BoundsSafety)
+ return; // Already an error under -fbounds-safety.
+
+ const auto *ME = dyn_cast<MemberExpr>(Op->IgnoreParens());
+ if (!ME)
+ return;
+
+ const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+ if (!FD)
+ return;
+
+ // The "whole FAM" shape is an incomplete array carrying a bounds attribute;
+ // this excludes &fam[idx], the decayed bare fam, and __counted_by pointers.
+ const auto *CATy = FD->getType()->getAs<CountAttributedType>();
+ if (!CATy || !CATy->desugar()->isIncompleteArrayType())
+ return;
+
+ auto DB = S.Diag(OpLoc, diag::warn_counted_by_addrof_discards_count)
+ << FD << static_cast<unsigned>(CATy->getKind())
+ << Op->getSourceRange();
+
+ // Offer "remove the '&'" only when the count is the useful bound.
+ if (!flexibleArrayMemberHasFixedStorage(ME))
+ DB << FixItHint::CreateRemoval(SourceRange(OpLoc, OpLoc));
+}
+
QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
if (const BuiltinType *PTy = OrigOp.get()->getType()->getAsPlaceholderType()){
if (PTy->getKind() == BuiltinType::Overload) {
@@ -15013,6 +15075,10 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
// Make sure to ignore parentheses in subsequent checks
Expr *op = OrigOp.get()->IgnoreParens();
+ // -Wcounted-by-addrof: '&' on a __counted_by flexible array member silently
+ // discards the count in CodeGen.
+ DiagnoseCountedByAddrOf(*this, OpLoc, op);
+
// In OpenCL captures for blocks called as lambda functions
// are located in the private address space. Blocks used in
// enqueue_kernel can be located in a different address space
diff --git a/clang/test/Sema/warn-counted-by-addrof.c b/clang/test/Sema/warn-counted-by-addrof.c
new file mode 100644
index 0000000000000..3b241ada42816
--- /dev/null
+++ b/clang/test/Sema/warn-counted-by-addrof.c
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=enabled -Wcounted-by-addrof %s
+// RUN: %clang_cc1 -fsyntax-only -verify=disabled %s
+// RUN: %clang_cc1 -fsyntax-only -Wcounted-by-addrof -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// disabled-no-diagnostics
+
+#define __counted_by(f) __attribute__((counted_by(f)))
+
+typedef __SIZE_TYPE__ size_t;
+
+struct annotated_flex {
+ size_t count;
+ char induce_padding;
+ char fam[] __counted_by(count);
+};
+
+struct plain_flex {
+ size_t count;
+ char fam[];
+};
+
+struct annotated_flex *get_ptr(void);
+
+size_t ptr_addrof(struct annotated_flex *p) {
+ return __builtin_dynamic_object_size(&p->fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+}
+
+// Opaque pointer return. Allocation not statically known. fix-it offered.
+size_t ret_addrof(void) {
+ return __builtin_dynamic_object_size(&get_ptr()->fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+}
+
+// Subscripting a pointer escapes to an unknown allocation. fix-it offered.
+size_t ptr_subscript_addrof(struct annotated_flex *parr, int i) {
+ return __builtin_dynamic_object_size(&parr[i].fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+}
+
+struct annotated_flex gaf;
+
+// Global (static storage). No fix-it.
+size_t global_addrof(void) {
+ return __builtin_dynamic_object_size(&gaf.fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+}
+
+// Local (automatic storage). No fix-it.
+size_t local_addrof(size_t n) {
+ struct annotated_flex af;
+ af.count = n;
+ return __builtin_dynamic_object_size(&af.fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+}
+
+// Static local (static storage, local scope). No fix-it.
+size_t static_local_addrof(void) {
+ static struct annotated_flex saf;
+ return __builtin_dynamic_object_size(&saf.fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+}
+
+size_t decayed(struct annotated_flex *p) {
+ // Decayed pointer-to-element honors the count. There is no '&'.
+ return __builtin_dynamic_object_size(p->fam, 1);
+}
+
+size_t element_addrof(struct annotated_flex *p, int i) {
+ // Address of an element, not the FAM-as-a-whole.
+ return __builtin_dynamic_object_size(&p->fam[i], 1);
+}
+
+char *count_addrof(struct annotated_flex *p) {
+ return (char *)&p->count; // non-FAM field.
+}
+
+char (*plain_addrof(struct plain_flex *p))[] {
+ return &p->fam; // FAM without counted_by.
+}
+
+// CHECK-COUNT-3: fix-it:{{.*}}:""
+// CHECK-NOT: fix-it:
\ No newline at end of file
>From 6f69ddc29a6e371e0d4cd80395a90a847f490a54 Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Sun, 19 Jul 2026 10:36:45 -0700
Subject: [PATCH 2/2] Switch to default-on and narrow the cases
---
clang/docs/ReleaseNotes.md | 10 +--
.../clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/Sema/SemaChecking.cpp | 83 +++++++++++++++++++
clang/lib/Sema/SemaExpr.cpp | 66 ---------------
clang/test/Sema/warn-counted-by-addrof.c | 40 +++++----
5 files changed, 115 insertions(+), 86 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 1a246419b7b0a..40bf338c18564 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -290,11 +290,11 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
against or converted to a null pointer, the same as a bare function name.
(#GH46362)
-- Added `-Wcounted-by-addrof` (default off) warning when unary `&` is applied to a `__counted_by`
- flexible array member as a whole (e.g. `&p->fam`). This form bypasses the count when lowering
- `__builtin_dynamic_object_size` and falls back to the object's static layout, silently discarding
- the annotation. A fix-it removes the `&` when the object's allocation is not statically known,
- where the count-derived bound is the useful answer.
+- Added the `-Wcounted-by-addrof` warning (on by default) for
+ `__builtin_dynamic_object_size(&p->fam, 1)` where `fam` is a `__counted_by` flexible array member.
+ Taking the array's address makes the builtin ignore the count and use the object's static layout
+ instead. The warning fires only when the array is reached through a pointer, where the count is the
+ useful bound; a fix-it removes the `&`.
### Improvements to Clang's time-trace
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b4cb0ff4e274b..9e74c528cfed3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7235,7 +7235,7 @@ def warn_counted_by_addrof_discards_count : Warning<
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' bound; "
"'__builtin_dynamic_object_size' on the resulting pointer uses the static "
"layout of the object, not the annotated count">,
- InGroup<CountedByAddrof>, DefaultIgnore;
+ InGroup<CountedByAddrof>;
// __builtin_counted_by_ref diagnostics:
def err_builtin_counted_by_ref_invalid_arg
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index ef07e8c6133ed..2c21e11fcbb1c 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3044,6 +3044,85 @@ static QualType getVectorElementType(ASTContext &Context, QualType VecTy) {
return QualType();
}
+/// Returns true when the object holding the flexible array member reached by
+/// \p ME has a fixed allocation (a local, parameter, static, or global
+/// variable). A count cannot enlarge a fixed allocation, so '&fam' loses
+/// nothing useful there. Returns false when the member is reached through a
+/// pointer, where the count is the bound the layout fallback would lose.
+static bool flexibleArrayMemberHasFixedStorage(const MemberExpr *ME) {
+ const Expr *E = ME;
+ while (true) {
+ if (const auto *M = dyn_cast<MemberExpr>(E)) {
+ if (M->isArrow())
+ return false; // Reached through a pointer.
+ E = M->getBase()->IgnoreParenImpCasts();
+ continue;
+ }
+ if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
+ const Expr *Base = ASE->getBase()->IgnoreParenImpCasts();
+ // Array subscript keeps the storage, pointer subscript escapes it.
+ if (Base->getType()->isPointerType())
+ return false;
+ E = Base;
+ continue;
+ }
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+ const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
+ // Locals, params, statics and globals all have a fixed allocation.
+ return VD && (VD->hasLocalStorage() || VD->hasGlobalStorage());
+ }
+ return false; // Call temporaries, opaque returns, etc.
+ }
+}
+
+/// -Wcounted-by-addrof: warn when the pointer argument of
+/// '__builtin_dynamic_object_size' is '&' applied to a whole '__counted_by'
+/// flexible array member (e.g. '__bdos(&p->fam, 1)'). Taking the array's
+/// address makes the builtin ignore the count and use the object's static
+/// layout instead. The warning fires only when the member is reached through a
+/// pointer: there the layout gives -1 ("unknown") and the count is the useful
+/// bound. When the object has a fixed allocation the layout answer is already
+/// correct, so we stay quiet.
+static void DiagnoseCountedByAddrOfDynamicObjectSize(Sema &S,
+ const CallExpr *Call) {
+ if (S.getLangOpts().BoundsSafety)
+ return; // Already an error under -fbounds-safety.
+
+ if (Call->getNumArgs() < 1)
+ return;
+
+ // The pointer argument must be the address of an lvalue: '&<expr>'.
+ const auto *UO = dyn_cast<UnaryOperator>(
+ Call->getArg(0)->IgnoreParenImpCasts());
+ if (!UO || UO->getOpcode() != UO_AddrOf)
+ return;
+
+ const auto *ME = dyn_cast<MemberExpr>(UO->getSubExpr()->IgnoreParens());
+ if (!ME)
+ return;
+
+ const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+ if (!FD)
+ return;
+
+ // Match a whole flexible array member: an incomplete array (T[]) with a
+ // __counted_by attribute. Excludes &fam[idx], the decayed 'fam', and
+ // __counted_by pointer fields.
+ const auto *CATy = FD->getType()->getAs<CountAttributedType>();
+ if (!CATy || !CATy->desugar()->isIncompleteArrayType())
+ return;
+
+ // A fixed allocation already gives the correct layout answer; nothing lost.
+ if (flexibleArrayMemberHasFixedStorage(ME))
+ return;
+
+ // Warn and offer to drop the '&' so the count-honoring decayed form is used.
+ S.Diag(UO->getOperatorLoc(), diag::warn_counted_by_addrof_discards_count)
+ << FD << static_cast<unsigned>(CATy->getKind()) << UO->getSourceRange()
+ << FixItHint::CreateRemoval(
+ SourceRange(UO->getOperatorLoc(), UO->getOperatorLoc()));
+}
+
ExprResult
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
CallExpr *TheCall) {
@@ -3267,6 +3346,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return ExprError();
break;
case Builtin::BI__builtin_dynamic_object_size:
+ if (BuiltinConstantArgRange(TheCall, 1, 0, 3))
+ return ExprError();
+ DiagnoseCountedByAddrOfDynamicObjectSize(*this, TheCall);
+ break;
case Builtin::BI__builtin_object_size:
if (BuiltinConstantArgRange(TheCall, 1, 0, 3))
return ExprError();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d6173a75fc24e..b844670543a55 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -14970,68 +14970,6 @@ bool Sema::CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc,
<< FixItHint::CreateInsertion(DRE->getSourceRange().getBegin(), Qual);
}
-/// Returns true if the object holding the FAM reached by \p ME has a
-/// statically-known allocation (automatic/static/global). There the layout
-/// answer __bdos already gives is the real bound, so we offer no fix-it. Only
-/// when the object is reached through a pointer does the count carry the useful
-/// bound and the "drop the '&'" fix-it apply.
-static bool flexibleArrayMemberHasFixedStorage(const MemberExpr *ME) {
- const Expr *E = ME;
- while (true) {
- if (const auto *M = dyn_cast<MemberExpr>(E)) {
- if (M->isArrow())
- return false; // Reached through a pointer.
- E = M->getBase()->IgnoreParenImpCasts();
- continue;
- }
- if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
- const Expr *Base = ASE->getBase()->IgnoreParenImpCasts();
- // Array subscript keeps the storage, pointer subscript escapes it.
- if (Base->getType()->isPointerType())
- return false;
- E = Base;
- continue;
- }
- if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
- const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
- // Locals, params, statics and globals all have a fixed allocation.
- return VD && (VD->hasLocalStorage() || VD->hasGlobalStorage());
- }
- return false; // Call temporaries, opaque returns, etc.
- }
-}
-
-/// -Wcounted-by-addrof: warn when unary '&' is applied to a whole
-/// '__counted_by' flexible array member. CodeGen bypasses emitCountedBySize()
-/// for this shape and falls back to layout-derived llvm.objectsize, discarding
-/// the count.
-static void DiagnoseCountedByAddrOf(Sema &S, SourceLocation OpLoc, Expr *Op) {
- if (S.getLangOpts().BoundsSafety)
- return; // Already an error under -fbounds-safety.
-
- const auto *ME = dyn_cast<MemberExpr>(Op->IgnoreParens());
- if (!ME)
- return;
-
- const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
- if (!FD)
- return;
-
- // The "whole FAM" shape is an incomplete array carrying a bounds attribute;
- // this excludes &fam[idx], the decayed bare fam, and __counted_by pointers.
- const auto *CATy = FD->getType()->getAs<CountAttributedType>();
- if (!CATy || !CATy->desugar()->isIncompleteArrayType())
- return;
-
- auto DB = S.Diag(OpLoc, diag::warn_counted_by_addrof_discards_count)
- << FD << static_cast<unsigned>(CATy->getKind())
- << Op->getSourceRange();
-
- // Offer "remove the '&'" only when the count is the useful bound.
- if (!flexibleArrayMemberHasFixedStorage(ME))
- DB << FixItHint::CreateRemoval(SourceRange(OpLoc, OpLoc));
-}
-
QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
if (const BuiltinType *PTy = OrigOp.get()->getType()->getAsPlaceholderType()){
if (PTy->getKind() == BuiltinType::Overload) {
@@ -15075,10 +15013,6 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
// Make sure to ignore parentheses in subsequent checks
Expr *op = OrigOp.get()->IgnoreParens();
- // -Wcounted-by-addrof: '&' on a __counted_by flexible array member silently
- // discards the count in CodeGen.
- DiagnoseCountedByAddrOf(*this, OpLoc, op);
-
// In OpenCL captures for blocks called as lambda functions
// are located in the private address space. Blocks used in
// enqueue_kernel can be located in a different address space
diff --git a/clang/test/Sema/warn-counted-by-addrof.c b/clang/test/Sema/warn-counted-by-addrof.c
index 3b241ada42816..0fa27bbd38fc7 100644
--- a/clang/test/Sema/warn-counted-by-addrof.c
+++ b/clang/test/Sema/warn-counted-by-addrof.c
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -fsyntax-only -verify=enabled -Wcounted-by-addrof %s
-// RUN: %clang_cc1 -fsyntax-only -verify=disabled %s
-// RUN: %clang_cc1 -fsyntax-only -Wcounted-by-addrof -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify=disabled -Wno-counted-by-addrof %s
+// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// disabled-no-diagnostics
@@ -22,37 +22,37 @@ struct plain_flex {
struct annotated_flex *get_ptr(void);
size_t ptr_addrof(struct annotated_flex *p) {
- return __builtin_dynamic_object_size(&p->fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+ return __builtin_dynamic_object_size(&p->fam, 1); // expected-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
}
// Opaque pointer return. Allocation not statically known. fix-it offered.
size_t ret_addrof(void) {
- return __builtin_dynamic_object_size(&get_ptr()->fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+ return __builtin_dynamic_object_size(&get_ptr()->fam, 1); // expected-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
}
// Subscripting a pointer escapes to an unknown allocation. fix-it offered.
size_t ptr_subscript_addrof(struct annotated_flex *parr, int i) {
- return __builtin_dynamic_object_size(&parr[i].fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+ return __builtin_dynamic_object_size(&parr[i].fam, 1); // expected-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
}
struct annotated_flex gaf;
-// Global (static storage). No fix-it.
+// Global: fixed allocation, so the layout answer is already correct. No warning.
size_t global_addrof(void) {
- return __builtin_dynamic_object_size(&gaf.fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+ return __builtin_dynamic_object_size(&gaf.fam, 1);
}
-// Local (automatic storage). No fix-it.
+// Local: fixed allocation. No warning.
size_t local_addrof(size_t n) {
struct annotated_flex af;
af.count = n;
- return __builtin_dynamic_object_size(&af.fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+ return __builtin_dynamic_object_size(&af.fam, 1);
}
-// Static local (static storage, local scope). No fix-it.
+// Static local (static storage, local scope). No warning.
size_t static_local_addrof(void) {
static struct annotated_flex saf;
- return __builtin_dynamic_object_size(&saf.fam, 1); // enabled-warning {{taking the address of flexible array member 'fam' discards the 'counted_by' bound}}
+ return __builtin_dynamic_object_size(&saf.fam, 1);
}
size_t decayed(struct annotated_flex *p) {
@@ -61,10 +61,22 @@ size_t decayed(struct annotated_flex *p) {
}
size_t element_addrof(struct annotated_flex *p, int i) {
- // Address of an element, not the FAM-as-a-whole.
+ // Address of an element, not the whole array.
return __builtin_dynamic_object_size(&p->fam[i], 1);
}
+// __builtin_object_size (non-dynamic) never consults the count, so &fam
+// discards nothing here.
+size_t static_object_size(struct annotated_flex *p) {
+ return __builtin_object_size(&p->fam, 1);
+}
+
+// Taking the address outside of __builtin_dynamic_object_size is not flagged:
+// the count is only silently discarded in the __bdos lowering.
+char (*bare_addrof(struct annotated_flex *p))[] {
+ return &p->fam;
+}
+
char *count_addrof(struct annotated_flex *p) {
return (char *)&p->count; // non-FAM field.
}
@@ -74,4 +86,4 @@ char (*plain_addrof(struct plain_flex *p))[] {
}
// CHECK-COUNT-3: fix-it:{{.*}}:""
-// CHECK-NOT: fix-it:
\ No newline at end of file
+// CHECK-NOT: fix-it:
More information about the cfe-commits
mailing list