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 <[email protected]>
Date: Tue, 30 Jun 2026 09:01:01 -0700
Subject: [PATCH 1/3] [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 <[email protected]>
Date: Sun, 19 Jul 2026 10:36:45 -0700
Subject: [PATCH 2/3] 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:

>From 3898b10a92fc1a2a9849b0e6926e2c329beddcac Mon Sep 17 00:00:00 2001
From: David Meng <[email protected]>
Date: Sun, 19 Jul 2026 13:06:27 -0700
Subject: [PATCH 3/3] Formatting

---
 clang/lib/Sema/SemaChecking.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2c21e11fcbb1c..e643f5edfe782 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3092,8 +3092,8 @@ static void DiagnoseCountedByAddrOfDynamicObjectSize(Sema 
&S,
     return;
 
   // The pointer argument must be the address of an lvalue: '&<expr>'.
-  const auto *UO = dyn_cast<UnaryOperator>(
-      Call->getArg(0)->IgnoreParenImpCasts());
+  const auto *UO =
+      dyn_cast<UnaryOperator>(Call->getArg(0)->IgnoreParenImpCasts());
   if (!UO || UO->getOpcode() != UO_AddrOf)
     return;
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to