https://github.com/isanbard updated 
https://github.com/llvm/llvm-project/pull/205903

>From 57b81573b38b1140c93f0294cd9a1bd217d2c9ca Mon Sep 17 00:00:00 2001
From: Bill Wendling <[email protected]>
Date: Wed, 24 Jun 2026 23:22:32 +0000
Subject: [PATCH 1/3] [Clang][counted_by] Use the expr's RecordDecl if
 available

---
 clang/lib/CodeGen/CGExpr.cpp                  |  21 ++-
 .../CodeGen/attr-counted-by-nested-structs.c  | 127 ++++++++++++++++++
 2 files changed, 142 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/CodeGen/attr-counted-by-nested-structs.c

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 465a020e38e74..bfe27bebbea9f 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1204,12 +1204,21 @@ llvm::Value *CodeGenFunction::GetCountedByFieldExprGEP(
   // Using getOuterLexicalRecordContext() here would be wrong because it walks
   // past named nested structs to the outermost record, causing a crash when a
   // struct with a counted_by FAM is defined nested inside another struct.
-  const RecordDecl *RD = CountDecl->getParent();
-  while (RD->isAnonymousStructOrUnion()) {
-    const auto *Parent = dyn_cast<RecordDecl>(RD->getLexicalParent());
-    if (!Parent)
-      break;
-    RD = Parent;
+  //
+  // The Base expression may itself be a pointer to a field of type 'struct'. 
If
+  // so, then we want to use that as the "base" RecordDecl.
+  QualType Ty = Base->IgnoreParenNoopCasts(getContext())->getType();
+  if (Ty->isPointerType())
+    Ty = Ty->getPointeeType();
+  const RecordDecl *RD = Ty->getAsRecordDecl();
+  if (!RD) {
+    RD = CountDecl->getParent();
+    while (RD->isAnonymousStructOrUnion()) {
+      const auto *Parent = dyn_cast<RecordDecl>(RD->getLexicalParent());
+      if (!Parent)
+        break;
+      RD = Parent;
+    }
   }
 
   // Find the base struct expr (i.e. p in p->a.b.c.d).
diff --git a/clang/test/CodeGen/attr-counted-by-nested-structs.c 
b/clang/test/CodeGen/attr-counted-by-nested-structs.c
new file mode 100644
index 0000000000000..954764fde2c79
--- /dev/null
+++ b/clang/test/CodeGen/attr-counted-by-nested-structs.c
@@ -0,0 +1,127 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wall 
-fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck %s
+
+#if !__has_attribute(counted_by)
+#error "has attribute broken"
+#endif
+
+#define __counted_by(member)   __attribute__((__counted_by__(member)))
+#define __bdos(P)              __builtin_dynamic_object_size(P, 0)
+
+struct flex {
+  unsigned count;
+  char a; /* force tail padding */
+  char fam[] __counted_by(count);
+};
+
+struct single_nested {
+  int header;
+  struct flex buf;
+};
+
+struct double_nested {
+  int header;
+  struct single_nested buf;
+};
+
+struct triple_nested {
+  int header;
+  struct double_nested buf;
+};
+
+struct quad_nested {
+  int header;
+  struct triple_nested buf;
+};
+
+// CHECK-LABEL: define dso_local i32 @test_size_of_single_nested(
+// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw 
[[STRUCT_SINGLE_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[BUF1:%.*]] = getelementptr inbounds nuw 
[[STRUCT_SINGLE_NESTED]], ptr [[TMP1]], i32 0, i32 1
+// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_FLEX:%.*]], ptr [[BUF1]], i32 0, i32 0, i32 1, i32 0
+// CHECK-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], 
align 4
+// CHECK-NEXT:    [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
+// CHECK-NEXT:    [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
+// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 12
+// CHECK-NEXT:    [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1
+// CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
+// CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP3]] to i32
+// CHECK-NEXT:    ret i32 [[CONV]]
+//
+unsigned test_size_of_single_nested(struct single_nested *p) {
+  return __bdos(&p->buf);
+}
+
+// CHECK-LABEL: define dso_local i32 @test_size_of_double_nested(
+// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw 
[[STRUCT_DOUBLE_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[BUF1:%.*]] = getelementptr inbounds nuw 
[[STRUCT_DOUBLE_NESTED]], ptr [[TMP1]], i32 0, i32 1
+// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_SINGLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 0, i32 1, i32 0
+// CHECK-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], 
align 4
+// CHECK-NEXT:    [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
+// CHECK-NEXT:    [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
+// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 16
+// CHECK-NEXT:    [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1
+// CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
+// CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP3]] to i32
+// CHECK-NEXT:    ret i32 [[CONV]]
+//
+unsigned test_size_of_double_nested(struct double_nested *p) {
+  return __bdos(&p->buf);
+}
+
+// CHECK-LABEL: define dso_local i32 @test_size_of_triple_nested(
+// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw 
[[STRUCT_TRIPLE_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[BUF1:%.*]] = getelementptr inbounds nuw 
[[STRUCT_TRIPLE_NESTED]], ptr [[TMP1]], i32 0, i32 1
+// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_DOUBLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 1, i32 0, i32 1, 
i32 0
+// CHECK-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], 
align 4
+// CHECK-NEXT:    [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
+// CHECK-NEXT:    [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
+// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 20
+// CHECK-NEXT:    [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1
+// CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
+// CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP3]] to i32
+// CHECK-NEXT:    ret i32 [[CONV]]
+//
+unsigned test_size_of_triple_nested(struct triple_nested *p) {
+  return __bdos(&p->buf);
+}
+
+// CHECK-LABEL: define dso_local i32 @test_size_of_quad_nested(
+// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw 
[[STRUCT_QUAD_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[BUF1:%.*]] = getelementptr inbounds nuw 
[[STRUCT_QUAD_NESTED]], ptr [[TMP1]], i32 0, i32 1
+// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_TRIPLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 1, i32 1, i32 0, 
i32 1, i32 0
+// CHECK-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], 
align 4
+// CHECK-NEXT:    [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
+// CHECK-NEXT:    [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
+// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 24
+// CHECK-NEXT:    [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1
+// CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
+// CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP3]] to i32
+// CHECK-NEXT:    ret i32 [[CONV]]
+//
+unsigned test_size_of_quad_nested(struct quad_nested *p) {
+  return __bdos(&p->buf);
+}

>From c6cb53434d035d427231f20bae6ccadc7794d256 Mon Sep 17 00:00:00 2001
From: Bill Wendling <[email protected]>
Date: Thu, 25 Jun 2026 20:42:33 +0000
Subject: [PATCH 2/3] Re-update the testcase.

---
 .../CodeGen/attr-counted-by-nested-structs.c     | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/clang/test/CodeGen/attr-counted-by-nested-structs.c 
b/clang/test/CodeGen/attr-counted-by-nested-structs.c
index 954764fde2c79..9f715add35872 100644
--- a/clang/test/CodeGen/attr-counted-by-nested-structs.c
+++ b/clang/test/CodeGen/attr-counted-by-nested-structs.c
@@ -43,11 +43,11 @@ struct quad_nested {
 // CHECK-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw 
[[STRUCT_SINGLE_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
 // CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
 // CHECK-NEXT:    [[BUF1:%.*]] = getelementptr inbounds nuw 
[[STRUCT_SINGLE_NESTED]], ptr [[TMP1]], i32 0, i32 1
-// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_FLEX:%.*]], ptr [[BUF1]], i32 0, i32 0, i32 1, i32 0
+// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_FLEX:%.*]], ptr [[BUF1]], i32 0, i32 0
 // CHECK-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], 
align 4
 // CHECK-NEXT:    [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
 // CHECK-NEXT:    [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
-// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 12
+// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 8
 // CHECK-NEXT:    [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1
 // CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
 // CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP3]] to i32
@@ -66,11 +66,11 @@ unsigned test_size_of_single_nested(struct single_nested 
*p) {
 // CHECK-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw 
[[STRUCT_DOUBLE_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
 // CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
 // CHECK-NEXT:    [[BUF1:%.*]] = getelementptr inbounds nuw 
[[STRUCT_DOUBLE_NESTED]], ptr [[TMP1]], i32 0, i32 1
-// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_SINGLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 0, i32 1, i32 0
+// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_SINGLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 0
 // CHECK-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], 
align 4
 // CHECK-NEXT:    [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
 // CHECK-NEXT:    [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
-// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 16
+// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 12
 // CHECK-NEXT:    [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1
 // CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
 // CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP3]] to i32
@@ -89,11 +89,11 @@ unsigned test_size_of_double_nested(struct double_nested 
*p) {
 // CHECK-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw 
[[STRUCT_TRIPLE_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
 // CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
 // CHECK-NEXT:    [[BUF1:%.*]] = getelementptr inbounds nuw 
[[STRUCT_TRIPLE_NESTED]], ptr [[TMP1]], i32 0, i32 1
-// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_DOUBLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 1, i32 0, i32 1, 
i32 0
+// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_DOUBLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 1, i32 0
 // CHECK-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], 
align 4
 // CHECK-NEXT:    [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
 // CHECK-NEXT:    [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
-// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 20
+// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 16
 // CHECK-NEXT:    [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1
 // CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
 // CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP3]] to i32
@@ -112,11 +112,11 @@ unsigned test_size_of_triple_nested(struct triple_nested 
*p) {
 // CHECK-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw 
[[STRUCT_QUAD_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
 // CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
 // CHECK-NEXT:    [[BUF1:%.*]] = getelementptr inbounds nuw 
[[STRUCT_QUAD_NESTED]], ptr [[TMP1]], i32 0, i32 1
-// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_TRIPLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 1, i32 1, i32 0, 
i32 1, i32 0
+// CHECK-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds 
[[STRUCT_TRIPLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 1, i32 1, i32 0
 // CHECK-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], 
align 4
 // CHECK-NEXT:    [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
 // CHECK-NEXT:    [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
-// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 24
+// CHECK-NEXT:    [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 20
 // CHECK-NEXT:    [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1
 // CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
 // CHECK-NEXT:    [[CONV:%.*]] = trunc i64 [[TMP3]] to i32

>From e28546cb948c0cb909f03759997ea6f970c92a3c Mon Sep 17 00:00:00 2001
From: Bill Wendling <[email protected]>
Date: Sun, 5 Jul 2026 20:33:17 -0700
Subject: [PATCH 3/3] Rework how we find the base Expr to build the GEP off of
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

  1. recordContainsField — new static helper that checks if CountDecl is
     reachable from RD through any level of nesting (mirrors what
     getGEPIndicesToField traverses).
  2. StructAccessBase — now takes const FieldDecl *CountDecl instead of
     const RecordDecl *ExpectedRD. IsExpectedRecordDecl uses
     recordContainsField so it matches any struct whose type
     transitively contains CountDecl, not just the one that directly
     holds it.
  3. GetCountedByFieldExprGEP — removed the two-path RD pre-computation
     (extract from Base's type, fall back to CountDecl's parent +
     anonymous struct walk). Now just calls
     StructAccessBase(CountDecl).Visit(Base) and derives RD from
     whatever expression the visitor found.
---
 clang/lib/CodeGen/CGExpr.cpp | 70 +++++++++++++++++++++---------------
 1 file changed, 42 insertions(+), 28 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index efc47c5bbfb1f..e53b4be55926a 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1060,6 +1060,23 @@ static llvm::Value 
*getArrayIndexingBound(CodeGenFunction &CGF,
 
 namespace {
 
+/// Returns true if \p Field is reachable from \p RD — either as a direct field
+/// or through a chain of nested record fields (including anonymous
+/// structs/unions). This mirrors the GEP path that getGEPIndicesToField 
builds,
+/// and is used to identify the right anchor expression in Base.
+static bool recordContainsField(const RecordDecl *RD,
+                                const FieldDecl *Field) {
+  for (const FieldDecl *FD : RD->fields()) {
+    if (FD == Field)
+      return true;
+    QualType Ty = FD->getType();
+    if (Ty->isRecordType())
+      if (recordContainsField(Ty->getAsRecordDecl(), Field))
+        return true;
+  }
+  return false;
+}
+
 /// \p StructAccessBase returns the base \p Expr of a field access. It returns
 /// either a \p DeclRefExpr, representing the base pointer to the struct, i.e.:
 ///
@@ -1078,17 +1095,24 @@ namespace {
 /// \p MemberExpr for \p p->ptr instead of \p p.
 class StructAccessBase
     : public ConstStmtVisitor<StructAccessBase, const Expr *> {
-  const RecordDecl *ExpectedRD;
-
+  /// The count field we're navigating to. We stop at the innermost expression
+  /// whose struct type transitively contains this field, so that
+  /// getGEPIndicesToField can navigate from that struct down to it.
+  const FieldDecl *CountDecl;
+
+  /// Returns true if E's record type (or pointee record type) transitively
+  /// contains CountDecl. Handles both direct containment and nested structs,
+  /// so we don't need a pre-computed RD from the caller.
   bool IsExpectedRecordDecl(const Expr *E) const {
     QualType Ty = E->getType();
     if (Ty->isPointerType())
       Ty = Ty->getPointeeType();
-    return ExpectedRD == Ty->getAsRecordDecl();
+    const RecordDecl *RD = Ty->getAsRecordDecl();
+    return RD && recordContainsField(RD, CountDecl);
   }
 
 public:
-  StructAccessBase(const RecordDecl *ExpectedRD) : ExpectedRD(ExpectedRD) {}
+  StructAccessBase(const FieldDecl *CountDecl) : CountDecl(CountDecl) {}
 
   
//===--------------------------------------------------------------------===//
   //                            Visitor Methods
@@ -1200,33 +1224,23 @@ static bool getGEPIndicesToField(CodeGenFunction &CGF, 
const RecordDecl *RD,
 
 llvm::Value *CodeGenFunction::GetCountedByFieldExprGEP(
     const Expr *Base, const FieldDecl *FAMDecl, const FieldDecl *CountDecl) {
-  // Find the record containing the count field. Walk up through anonymous
-  // structs/unions (which are transparent in C) but stop at named records.
-  // Using getOuterLexicalRecordContext() here would be wrong because it walks
-  // past named nested structs to the outermost record, causing a crash when a
-  // struct with a counted_by FAM is defined nested inside another struct.
-  //
-  // The Base expression may itself be a pointer to a field of type 'struct'. 
If
-  // so, then we want to use that as the "base" RecordDecl.
-  QualType Ty = Base->IgnoreParenNoopCasts(getContext())->getType();
-  if (Ty->isPointerType())
-    Ty = Ty->getPointeeType();
-  const RecordDecl *RD = Ty->getAsRecordDecl();
-  if (!RD) {
-    RD = CountDecl->getParent();
-    while (RD->isAnonymousStructOrUnion()) {
-      const auto *Parent = dyn_cast<RecordDecl>(RD->getLexicalParent());
-      if (!Parent)
-        break;
-      RD = Parent;
-    }
-  }
-
-  // Find the base struct expr (i.e. p in p->a.b.c.d).
-  const Expr *StructBase = StructAccessBase(RD).Visit(Base);
+  // Walk Base to find the deepest sub-expression whose struct type 
transitively
+  // contains CountDecl. This is our GEP anchor — getGEPIndicesToField then
+  // builds the field indices from that struct down to CountDecl, handling any
+  // intermediate nesting without requiring us to pre-compute a RecordDecl from
+  // Base's type or from CountDecl's parent chain.
+  const Expr *StructBase = StructAccessBase(CountDecl).Visit(Base);
   if (!StructBase || StructBase->HasSideEffects(getContext()))
     return nullptr;
 
+  // Derive the record type from the anchor expression itself.
+  QualType StructTy = StructBase->getType();
+  if (StructTy->isPointerType())
+    StructTy = StructTy->getPointeeType();
+  const RecordDecl *RD = StructTy->getAsRecordDecl();
+  if (!RD)
+    return nullptr;
+
   llvm::Value *Res = nullptr;
   if (StructBase->getType()->isPointerType()) {
     LValueBaseInfo BaseInfo;

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

Reply via email to