[clang] [clang][ObjC] Restrict DIProperty backing-storage emission to synthesized accessor stubs (PR #227771)
Piotr Jeremicz via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 10 08:24:02 PDT 2026
https://github.com/piotrekjeremicz updated https://github.com/llvm/llvm-project/pull/227771
>From e378672117101cabe5452542a106b9cddbb34947 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 28 Sep 2026 14:22:13 +0200
Subject: [PATCH 1/2] [clang][ObjC] Only describe a property's ivar as backing
storage when accessors are synthesized stubs
---
clang/lib/CodeGen/CGDebugInfo.cpp | 6 +++-
.../DebugInfo/ObjC/property-backing-storage.m | 35 +++++++++++++++++--
2 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index b6418753a6a65..66efb12b35d64 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -3820,12 +3820,16 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty,
if (ObjCPropertyImplDecl *PImpD =
ImpD->FindPropertyImplIvarDecl(Field->getIdentifier())) {
if (ObjCPropertyDecl *PD = PImpD->getPropertyDecl()) {
- SynthesizedProperty = PD;
SourceLocation Loc = PD->getLocation();
PUnit = getOrCreateFile(Loc);
PLine = getLineNumber(Loc);
ObjCMethodDecl *Getter = PImpD->getGetterMethodDecl();
ObjCMethodDecl *Setter = PImpD->getSetterMethodDecl();
+ bool GetterIsStub = !Getter || Getter->isSynthesizedAccessorStub();
+ bool SetterIsStub = PD->isReadOnly() || !Setter ||
+ Setter->isSynthesizedAccessorStub();
+ if (GetterIsStub && SetterIsStub)
+ SynthesizedProperty = PD;
PropertyNode = DBuilder.createObjCProperty(
PD->getName(), PUnit, PLine,
hasDefaultGetterName(PD, Getter)
diff --git a/clang/test/DebugInfo/ObjC/property-backing-storage.m b/clang/test/DebugInfo/ObjC/property-backing-storage.m
index 456926e3a9dd4..76117aca3b854 100644
--- a/clang/test/DebugInfo/ObjC/property-backing-storage.m
+++ b/clang/test/DebugInfo/ObjC/property-backing-storage.m
@@ -9,8 +9,14 @@
// 3. implicitBacking - no @synthesize at all; the compiler
// auto-synthesizes both the accessors and a
// default-named ivar (_implicitBacking).
+// It also verifies that no DIProperty is emitted when an accessor is
+// hand-written (class HandwrittenAccessor), because such an accessor may do
+// more than forward to the ivar, so a debugger must not bypass it. The legacy
+// DIObjCProperty is still emitted in that case.
-// RUN: %clang_cc1 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -debug-info-kind=limited %s -o %t.ll
+// RUN: FileCheck %s < %t.ll
+// RUN: FileCheck %s --check-prefix=NO-DIPROPERTY < %t.ll
// CHECK-DAG: ![[DECLARED_PROP:[0-9]+]] = !DIObjCProperty(name: "declaredBacking", file: ![[FILE:[0-9]+]], line: {{[0-9]+}}, attributes: 2316, type: ![[INT_TY:[0-9]+]])
// CHECK-DAG: ![[UNDECLARED_PROP:[0-9]+]] = !DIObjCProperty(name: "undeclaredBacking", file: ![[FILE]], line: {{[0-9]+}}, attributes: 2316, type: ![[INT_TY]])
@@ -25,6 +31,19 @@
// CHECK-DAG: ![[IMPLICIT_IVAR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "_implicitBacking", {{.*}}file: ![[FILE]], {{.*}}extraData: ![[IMPLICIT_PROP]])
// CHECK-DAG: !DIProperty(name: "implicitBacking", file: ![[FILE]], line: {{[0-9]+}}, type: ![[INT_TY]], backing_storage: ![[IMPLICIT_IVAR]])
+//
+// Hand-written accessors: the legacy DIObjCProperty is still emitted and
+// linked from the ivar...
+// CHECK-DAG: ![[COMPUTED_PROP:[0-9]+]] = !DIObjCProperty(name: "computed", file: ![[FILE]], line: {{[0-9]+}}, attributes: 2124, type: ![[INT_TY]])
+// CHECK-DAG: !DIDerivedType(tag: DW_TAG_member, name: "_computed", {{.*}}file: ![[FILE]], {{.*}}extraData: ![[COMPUTED_PROP]])
+// CHECK-DAG: ![[CLAMPED_PROP:[0-9]+]] = !DIObjCProperty(name: "clamped", file: ![[FILE]], line: {{[0-9]+}}, attributes: 2124, type: ![[INT_TY]])
+// CHECK-DAG: !DIDerivedType(tag: DW_TAG_member, name: "_clamped", {{.*}}file: ![[FILE]], {{.*}}extraData: ![[CLAMPED_PROP]])
+//
+// ...but no DIProperty is. These use a separate prefix with only NOT lines so
+// they scan the whole output, not just the text after the last CHECK-DAG match.
+// NO-DIPROPERTY-NOT: !DIProperty(name: "computed"
+// NO-DIPROPERTY-NOT: !DIProperty(name: "clamped"
+
@interface C {
int _customDeclaredIvar;
}
@@ -38,4 +57,16 @@ @implementation C
@synthesize undeclaredBacking = _customUndeclaredIvar;
@end
-void foo(C *cptr) {}
+ at interface HandwrittenAccessor
+// Hand-written getter, synthesized setter.
+ at property(nonatomic) int computed;
+// Synthesized getter, hand-written setter.
+ at property(nonatomic) int clamped;
+ at end
+
+ at implementation HandwrittenAccessor
+- (int)computed { return _computed * 100; }
+- (void)setClamped:(int)clamped { _clamped = clamped > 10 ? 10 : clamped; }
+ at end
+
+void foo(C *cptr, HandwrittenAccessor *hptr) {}
>From fe64c6bff7d1e2ad0b57e6f29da986ad0fae51ad Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Sat, 10 Oct 2026 17:23:51 +0200
Subject: [PATCH 2/2] Removed comments
---
clang/test/DebugInfo/ObjC/property-backing-storage.m | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/clang/test/DebugInfo/ObjC/property-backing-storage.m b/clang/test/DebugInfo/ObjC/property-backing-storage.m
index 76117aca3b854..71be8121d3bdc 100644
--- a/clang/test/DebugInfo/ObjC/property-backing-storage.m
+++ b/clang/test/DebugInfo/ObjC/property-backing-storage.m
@@ -9,10 +9,6 @@
// 3. implicitBacking - no @synthesize at all; the compiler
// auto-synthesizes both the accessors and a
// default-named ivar (_implicitBacking).
-// It also verifies that no DIProperty is emitted when an accessor is
-// hand-written (class HandwrittenAccessor), because such an accessor may do
-// more than forward to the ivar, so a debugger must not bypass it. The legacy
-// DIObjCProperty is still emitted in that case.
// RUN: %clang_cc1 -emit-llvm -debug-info-kind=limited %s -o %t.ll
// RUN: FileCheck %s < %t.ll
@@ -31,16 +27,11 @@
// CHECK-DAG: ![[IMPLICIT_IVAR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "_implicitBacking", {{.*}}file: ![[FILE]], {{.*}}extraData: ![[IMPLICIT_PROP]])
// CHECK-DAG: !DIProperty(name: "implicitBacking", file: ![[FILE]], line: {{[0-9]+}}, type: ![[INT_TY]], backing_storage: ![[IMPLICIT_IVAR]])
-//
-// Hand-written accessors: the legacy DIObjCProperty is still emitted and
-// linked from the ivar...
// CHECK-DAG: ![[COMPUTED_PROP:[0-9]+]] = !DIObjCProperty(name: "computed", file: ![[FILE]], line: {{[0-9]+}}, attributes: 2124, type: ![[INT_TY]])
// CHECK-DAG: !DIDerivedType(tag: DW_TAG_member, name: "_computed", {{.*}}file: ![[FILE]], {{.*}}extraData: ![[COMPUTED_PROP]])
// CHECK-DAG: ![[CLAMPED_PROP:[0-9]+]] = !DIObjCProperty(name: "clamped", file: ![[FILE]], line: {{[0-9]+}}, attributes: 2124, type: ![[INT_TY]])
// CHECK-DAG: !DIDerivedType(tag: DW_TAG_member, name: "_clamped", {{.*}}file: ![[FILE]], {{.*}}extraData: ![[CLAMPED_PROP]])
-//
-// ...but no DIProperty is. These use a separate prefix with only NOT lines so
-// they scan the whole output, not just the text after the last CHECK-DAG match.
+
// NO-DIPROPERTY-NOT: !DIProperty(name: "computed"
// NO-DIPROPERTY-NOT: !DIProperty(name: "clamped"
More information about the cfe-commits
mailing list