[Lldb-commits] [lldb] [lldb] Resolve ObjC property backing storage in dwim-print (PR #225204)
Piotr Jeremicz via lldb-commits
lldb-commits at lists.llvm.org
Mon Sep 21 14:48:26 PDT 2026
https://github.com/piotrekjeremicz updated https://github.com/llvm/llvm-project/pull/225204
>From 780ae8ef6d73e058210962fd17de6c0dc32bffa1 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Fri, 11 Sep 2026 14:39:07 +0200
Subject: [PATCH 1/5] [lldb][DWARF] Attach synthesized ivar to Objective-C
property decls
---
.../SymbolFile/DWARF/DWARFASTParserClang.cpp | 72 +++++++++++++++++--
.../SymbolFile/DWARF/DWARFASTParserClang.h | 9 +++
.../TypeSystem/Clang/TypeSystemClang.cpp | 15 ++--
3 files changed, 81 insertions(+), 15 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index b60f1d9e41958a..c039aa3234a07f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -48,6 +48,7 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
#include "llvm/DebugInfo/DWARF/DWARFTypePrinter.h"
#include "llvm/Demangle/Demangle.h"
@@ -1983,21 +1984,41 @@ class DWARFASTParserClang::DelayedAddObjCClassProperty {
// required if you don't have an
// ivar decl
const char *property_setter_name, const char *property_getter_name,
- uint32_t property_attributes, ClangASTMetadata metadata)
+ uint32_t property_attributes, ClangASTMetadata metadata,
+ llvm::StringRef property_ivar_name)
: m_class_opaque_type(class_opaque_type), m_property_name(property_name),
m_property_opaque_type(property_opaque_type),
m_property_setter_name(property_setter_name),
m_property_getter_name(property_getter_name),
- m_property_attributes(property_attributes), m_metadata(metadata) {}
+ m_property_attributes(property_attributes), m_metadata(metadata),
+ m_property_ivar_name(property_ivar_name) {}
bool Finalize() {
return TypeSystemClang::AddObjCClassProperty(
m_class_opaque_type, m_property_name, m_property_opaque_type,
- /*ivar_decl=*/nullptr, m_property_setter_name, m_property_getter_name,
+ FindIvarDecl(), m_property_setter_name, m_property_getter_name,
m_property_attributes, m_metadata);
}
private:
+ clang::ObjCIvarDecl *FindIvarDecl() {
+ if (m_property_ivar_name.empty())
+ return nullptr;
+
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ TypeSystemClang::GetAsObjCInterfaceDecl(m_class_opaque_type);
+ if (!class_interface_decl)
+ return nullptr;
+
+ auto ast = m_class_opaque_type.GetTypeSystem<TypeSystemClang>();
+ if (!ast)
+ return nullptr;
+
+ clang::IdentifierInfo &ivar_ident =
+ ast->getASTContext().Idents.get(m_property_ivar_name);
+ return class_interface_decl->lookupInstanceVariable(&ivar_ident);
+ }
+
CompilerType m_class_opaque_type;
const char *m_property_name;
CompilerType m_property_opaque_type;
@@ -2005,6 +2026,7 @@ class DWARFASTParserClang::DelayedAddObjCClassProperty {
const char *m_property_getter_name;
uint32_t m_property_attributes;
ClangASTMetadata m_metadata;
+ llvm::StringRef m_property_ivar_name;
};
static std::optional<clang::APValue> MakeAPValue(const clang::ASTContext &ast,
@@ -2831,9 +2853,39 @@ PropertyAttributes::PropertyAttributes(const DWARFDIE &die) {
}
}
+DWARFASTParserClang::PropertyBackingStorageNames
+DWARFASTParserClang::ParsePropertyBackingStorageNames(
+ const DWARFDIE &parent_die) {
+ PropertyBackingStorageNames property_backing_names;
+
+ for (DWARFDIE die : parent_die.children()) {
+ if (die.Tag() != DW_TAG_property)
+ continue;
+
+ const char *prop_name = die.GetName();
+ if (!prop_name)
+ continue;
+
+ for (DWARFDIE child_die : die.children()) {
+ if (child_die.Tag() != DW_TAG_property_getter)
+ continue;
+
+ if (DWARFDIE backing_die = child_die.GetAttributeValueAsReferenceDIE(
+ DW_AT_property_forward)) {
+ if (const char *backing_name = backing_die.GetName())
+ property_backing_names[prop_name] = backing_name;
+ }
+ break;
+ }
+ }
+
+ return property_backing_names;
+}
+
void DWARFASTParserClang::ParseObjCProperty(
const DWARFDIE &die, const DWARFDIE &parent_die,
const lldb_private::CompilerType &class_clang_type,
+ const PropertyBackingStorageNames &property_backing_names,
DelayedPropertyList &delayed_properties) {
// This function can only parse DW_TAG_APPLE_property.
assert(die.Tag() == DW_TAG_APPLE_property);
@@ -2859,12 +2911,18 @@ void DWARFASTParserClang::ParseObjCProperty(
return;
}
+ llvm::StringRef property_ivar_name;
+ auto backing_name_it = property_backing_names.find(propAttrs.prop_name);
+ if (backing_name_it != property_backing_names.end())
+ property_ivar_name = backing_name_it->second;
+
ClangASTMetadata metadata;
metadata.SetUserID(die.GetID());
delayed_properties.emplace_back(
class_clang_type, propAttrs.prop_name,
member_type->GetLayoutCompilerType(), propAttrs.prop_setter_name,
- propAttrs.prop_getter_name, propAttrs.prop_attributes, metadata);
+ propAttrs.prop_getter_name, propAttrs.prop_attributes, metadata,
+ property_ivar_name);
}
llvm::Expected<llvm::APInt> DWARFASTParserClang::ExtractIntFromFormValue(
@@ -3140,12 +3198,16 @@ bool DWARFASTParserClang::ParseChildMembers(
if (ast == nullptr)
return false;
+ const PropertyBackingStorageNames property_backing_names =
+ ParsePropertyBackingStorageNames(parent_die);
+
for (DWARFDIE die : parent_die.children()) {
dw_tag_t tag = die.Tag();
switch (tag) {
case DW_TAG_APPLE_property:
- ParseObjCProperty(die, parent_die, class_clang_type, delayed_properties);
+ ParseObjCProperty(die, parent_die, class_clang_type,
+ property_backing_names, delayed_properties);
break;
case DW_TAG_variant_part:
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
index 03c431c73fb6ff..1d4569a7588eca 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -14,6 +14,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
#include "DWARFASTParser.h"
#include "DWARFDIE.h"
@@ -137,6 +138,8 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
class DelayedAddObjCClassProperty;
typedef std::vector<DelayedAddObjCClassProperty> DelayedPropertyList;
+ typedef llvm::StringMap<llvm::StringRef> PropertyBackingStorageNames;
+
typedef llvm::DenseMap<
const lldb_private::plugin::dwarf::DWARFDebugInfoEntry *,
clang::DeclContext *>
@@ -204,6 +207,9 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
DelayedPropertyList &delayed_properties,
lldb_private::ClangASTImporter::LayoutInfo &layout_info);
+ PropertyBackingStorageNames ParsePropertyBackingStorageNames(
+ const lldb_private::plugin::dwarf::DWARFDIE &parent_die);
+
void ParseChildParameters(
clang::DeclContext *containing_decl_ctx,
const lldb_private::plugin::dwarf::DWARFDIE &parent_die,
@@ -399,12 +405,15 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
/// \param parent_die The parent DIE.
/// \param class_clang_type The Objective-C class that will contain the
/// created property.
+ /// \param property_backing_names Map from property name to backing member
+ /// name, as produced by ParsePropertyBackingStorageNames.
/// \param delayed_properties The list of delayed properties that the result
/// will be appended to.
void
ParseObjCProperty(const lldb_private::plugin::dwarf::DWARFDIE &die,
const lldb_private::plugin::dwarf::DWARFDIE &parent_die,
const lldb_private::CompilerType &class_clang_type,
+ const PropertyBackingStorageNames &property_backing_names,
DelayedPropertyList &delayed_properties);
void
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 5255ec835c0a44..ac84a66b3a129d 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -7939,21 +7939,16 @@ bool TypeSystemClang::AddObjCClassProperty(
if (!class_interface_decl || !property_clang_type_to_access.IsValid())
return false;
- clang::TypeSourceInfo *prop_type_source;
- if (ivar_decl)
- prop_type_source = clang_ast.getTrivialTypeSourceInfo(ivar_decl->getType());
- else
- prop_type_source = clang_ast.getTrivialTypeSourceInfo(
- ClangUtil::GetQualType(property_clang_type));
+ clang::QualType property_qual_type =
+ ClangUtil::GetQualType(property_clang_type_to_access);
+ clang::TypeSourceInfo *prop_type_source =
+ clang_ast.getTrivialTypeSourceInfo(property_qual_type);
clang::ObjCPropertyDecl *property_decl =
clang::ObjCPropertyDecl::CreateDeserialized(clang_ast, GlobalDeclID());
property_decl->setDeclContext(class_interface_decl);
property_decl->setDeclName(&clang_ast.Idents.get(property_name));
- property_decl->setType(ivar_decl
- ? ivar_decl->getType()
- : ClangUtil::GetQualType(property_clang_type),
- prop_type_source);
+ property_decl->setType(property_qual_type, prop_type_source);
SetMemberOwningModule(property_decl, class_interface_decl);
if (!property_decl)
>From 37c191ca4cbc041c650ba16e0ebf47ff18bf16f1 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Fri, 11 Sep 2026 22:18:40 +0200
Subject: [PATCH 2/5] [lldb] Add CompilerType::GetPropertyBackingStorageName
for ObjC properties
---
lldb/include/lldb/Symbol/CompilerType.h | 4 +++
lldb/include/lldb/Symbol/TypeSystem.h | 12 +++++++
.../TypeSystem/Clang/TypeSystemClang.cpp | 35 +++++++++++++++++++
.../TypeSystem/Clang/TypeSystemClang.h | 4 +++
lldb/source/Symbol/CompilerType.cpp | 10 ++++++
5 files changed, 65 insertions(+)
diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index 168946cf17475e..ffeacd49ffa69b 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -485,6 +485,10 @@ class CompilerType {
CompilerType GetDirectNestedTypeWithName(llvm::StringRef name) const;
+ /// See TypeSystem::GetPropertyBackingStorageName.
+ llvm::StringRef
+ GetPropertyBackingStorageName(llvm::StringRef property_name) const;
+
/// Return the number of template arguments the type has.
/// If expand_pack is true, then variadic argument packs are automatically
/// expanded to their supplied arguments. If it is false an argument pack
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 9ec0f13814975a..3c141b9fc0dd3d 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -403,6 +403,18 @@ class TypeSystem : public PluginInterface,
return CompilerType();
}
+ /// Returns the name of the member that backs the named property on
+ /// \a type, if this type system knows of one (e.g. an ivar reached via
+ /// DW_AT_property_forward on a DWARF DW_TAG_property).
+ ///
+ /// \return An empty string if \a type has no property named
+ /// \a property_name, or if that property has no known backing storage.
+ virtual llvm::StringRef
+ GetPropertyBackingStorageName(lldb::opaque_compiler_type_t type,
+ llvm::StringRef property_name) {
+ return llvm::StringRef();
+ }
+
virtual bool IsTemplateType(lldb::opaque_compiler_type_t type);
virtual size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type,
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index ac84a66b3a129d..2f4b4a65433763 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -7090,6 +7090,41 @@ TypeSystemClang::GetDirectNestedTypeWithName(lldb::opaque_compiler_type_t type,
return CompilerType();
}
+llvm::StringRef TypeSystemClang::GetPropertyBackingStorageName(
+ lldb::opaque_compiler_type_t type, llvm::StringRef property_name) {
+ if (!type || property_name.empty())
+ return llvm::StringRef();
+
+ CompilerType compiler_type(weak_from_this(), type);
+
+ // Callers will usually hand us an object pointer (e.g. `Foo *`); strip it
+ // to get at the interface itself.
+ CompilerType class_type;
+ if (IsObjCObjectPointerType(compiler_type, &class_type))
+ compiler_type = class_type;
+
+ if (!GetCompleteType(compiler_type.GetOpaqueQualType()))
+ return llvm::StringRef();
+
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ GetAsObjCInterfaceDecl(compiler_type);
+ if (!class_interface_decl)
+ return llvm::StringRef();
+
+ clang::IdentifierInfo &property_ident =
+ getASTContext().Idents.get(property_name);
+ clang::ObjCPropertyDecl *property_decl =
+ class_interface_decl->FindPropertyDeclaration(
+ &property_ident, clang::ObjCPropertyQueryKind::OBJC_PR_query_instance);
+ if (!property_decl)
+ return llvm::StringRef();
+
+ if (clang::ObjCIvarDecl *ivar_decl = property_decl->getPropertyIvarDecl())
+ return ivar_decl->getName();
+
+ return llvm::StringRef();
+}
+
bool TypeSystemClang::IsTemplateType(lldb::opaque_compiler_type_t type) {
if (!type)
return false;
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 28e79f2b0b11f8..cfeac83d3c8398 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -927,6 +927,10 @@ class TypeSystemClang : public TypeSystem {
CompilerType GetDirectNestedTypeWithName(lldb::opaque_compiler_type_t type,
llvm::StringRef name) override;
+ llvm::StringRef
+ GetPropertyBackingStorageName(lldb::opaque_compiler_type_t type,
+ llvm::StringRef property_name) override;
+
bool IsTemplateType(lldb::opaque_compiler_type_t type) override;
size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type,
diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp
index 6f20bfa89ed38c..8e6f7c03191d5d 100644
--- a/lldb/source/Symbol/CompilerType.cpp
+++ b/lldb/source/Symbol/CompilerType.cpp
@@ -970,6 +970,16 @@ CompilerType::GetDirectNestedTypeWithName(llvm::StringRef name) const {
return CompilerType();
}
+llvm::StringRef
+CompilerType::GetPropertyBackingStorageName(llvm::StringRef property_name) const {
+ if (IsValid() && !property_name.empty()) {
+ if (auto type_system_sp = GetTypeSystem())
+ return type_system_sp->GetPropertyBackingStorageName(m_type,
+ property_name);
+ }
+ return llvm::StringRef();
+}
+
size_t CompilerType::GetNumTemplateArguments(bool expand_pack) const {
if (IsValid()) {
if (auto type_system_sp = GetTypeSystem())
>From 1912b436ff0717383f593d0da562de1f6936dd22 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 21 Sep 2026 23:00:23 +0200
Subject: [PATCH 3/5] [lldb] Teach dwim-print to resolve ObjC property backing
storage
---
.../Commands/CommandObjectDWIMPrint.cpp | 87 +++++++++++++++++--
lldb/source/Commands/CommandObjectDWIMPrint.h | 6 ++
2 files changed, 84 insertions(+), 9 deletions(-)
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 1b0b4c7881cfc1..9df520e4b4089d 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -22,6 +22,7 @@
#include "lldb/lldb-defines.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <regex>
@@ -51,6 +52,54 @@ CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter &interpreter)
Options *CommandObjectDWIMPrint::GetOptions() { return &m_option_group; }
+std::string CommandObjectDWIMPrint::RewritePathForBackingStorage(
+ llvm::StringRef expr, StackFrame &frame,
+ lldb::DynamicValueType use_dynamic) {
+ llvm::SmallVector<llvm::StringRef, 4> components;
+ expr.split(components, '.');
+ if (components.empty())
+ return {};
+
+ VariableSP var_sp;
+ Status status;
+ ValueObjectSP valobj_sp = frame.GetValueForVariableExpressionPath(
+ components[0], use_dynamic,
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
+ StackFrame::eExpressionPathOptionsDisallowGlobals,
+ var_sp, status, lldb::eDILModeSimple);
+ if (!valobj_sp || !status.Success() || valobj_sp->GetError().Fail())
+ return {};
+
+ std::string rewritten_path = components[0].str();
+ bool did_rewrite = false;
+
+ for (llvm::StringRef component :
+ llvm::ArrayRef(components).drop_front()) {
+ ValueObjectSP child_sp = valobj_sp->GetChildMemberWithName(component);
+ llvm::StringRef name_used = component;
+ if (!child_sp) {
+ llvm::StringRef backing_name =
+ valobj_sp->GetCompilerType().GetPropertyBackingStorageName(
+ component);
+ if (backing_name.empty())
+ return {};
+ child_sp = valobj_sp->GetChildMemberWithName(backing_name);
+ if (!child_sp)
+ return {};
+ name_used = backing_name;
+ did_rewrite = true;
+ }
+ rewritten_path += '.';
+ rewritten_path += name_used;
+ valobj_sp = child_sp;
+ }
+
+ if (!did_rewrite)
+ return {};
+
+ return rewritten_path;
+}
+
void CommandObjectDWIMPrint::DoExecute(StringRef command,
CommandReturnObject &result) {
m_option_group.NotifyOptionParsingStarting(&m_exe_ctx);
@@ -165,14 +214,34 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
const bool try_variable_path =
expr.find_first_of("*&->[]") == StringRef::npos;
if (frame && try_variable_path) {
- VariableSP var_sp;
- Status status;
- auto valobj_sp = frame->GetValueForVariableExpressionPath(
- expr, eval_options.GetUseDynamic(),
- StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
- StackFrame::eExpressionPathOptionsDisallowGlobals,
- var_sp, status, lldb::eDILModeSimple);
- if (valobj_sp && status.Success() && valobj_sp->GetError().Success()) {
+ auto try_variable_expr = [&](llvm::StringRef path) -> ValueObjectSP {
+ VariableSP var_sp;
+ Status status;
+ auto valobj_sp = frame->GetValueForVariableExpressionPath(
+ path, eval_options.GetUseDynamic(),
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
+ StackFrame::eExpressionPathOptionsDisallowGlobals,
+ var_sp, status, lldb::eDILModeSimple);
+ if (valobj_sp && status.Success() && valobj_sp->GetError().Success())
+ return valobj_sp;
+ return nullptr;
+ };
+
+ StringRef used_path = expr;
+ ValueObjectSP valobj_sp = try_variable_expr(expr);
+
+ std::string rewritten_path;
+ if (!valobj_sp) {
+ rewritten_path = RewritePathForBackingStorage(
+ expr, *frame, eval_options.GetUseDynamic());
+ if (!rewritten_path.empty()) {
+ valobj_sp = try_variable_expr(rewritten_path);
+ if (valobj_sp)
+ used_path = rewritten_path;
+ }
+ }
+
+ if (valobj_sp) {
if (!suppress_result) {
if (auto persisted_valobj = valobj_sp->Persist())
valobj_sp = persisted_valobj;
@@ -183,7 +252,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
if (args.HasArgs())
flags = args.GetArgString();
result.AppendNoteWithFormatv("ran `frame variable {0}{1}`", flags,
- expr);
+ used_path);
}
dump_val_object(*valobj_sp);
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.h b/lldb/source/Commands/CommandObjectDWIMPrint.h
index 01ba9c225e3301..b5e82bb692eac1 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.h
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.h
@@ -17,6 +17,8 @@
namespace lldb_private {
+class StackFrame;
+
/// Implements `dwim-print`, a printing command that chooses the most direct,
/// efficient, and resilient means of printing a given expression.
///
@@ -42,6 +44,10 @@ class CommandObjectDWIMPrint : public CommandObjectRaw {
private:
void DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
+ std::string
+ RewritePathForBackingStorage(llvm::StringRef expr, StackFrame &frame,
+ lldb::DynamicValueType use_dynamic);
+
OptionGroupOptions m_option_group;
OptionGroupFormat m_format_options = lldb::eFormatDefault;
OptionGroupValueObjectDisplay m_varobj_options;
>From 81b5c6909c49943a317d64c00166abb5e01781c7 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 21 Sep 2026 23:32:16 +0200
Subject: [PATCH 4/5] [lldb] Added DWIM tests
---
.../dwim-print/objc/TestDWIMPrintObjC.py | 26 +++++++++++++++++++
lldb/test/API/commands/dwim-print/objc/main.m | 20 ++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py b/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
index a25a1925272933..1ff57a2fd061f9 100644
--- a/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
+++ b/lldb/test/API/commands/dwim-print/objc/TestDWIMPrintObjC.py
@@ -25,3 +25,29 @@ def test_with_summary(self):
self.runCmd("type summary add -s 'Parent of ${var._child._name}' 'Parent *'")
self.expect("dwim-print parent", matching=False, substrs=["_child = 0x"])
self.expect("dwim-print parent", substrs=['Parent of @"Seven"'])
+
+ @requireDarwin
+ def test_property_backing_storage(self):
+ """A property backed by a differently-named ivar (DW_TAG_property /
+ DW_AT_property_forward) should take the frame-variable fast path,
+ instead of unconditionally falling back to `expression`. Covers the
+ same three synthesis variants as
+ clang/test/DebugInfo/ObjC/property-backing-storage.m."""
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "break here for backing storage", lldb.SBFileSpec("main.m")
+ )
+ self.runCmd("settings set dwim-print-verbosity full")
+
+ self.expect(
+ "dwim-print obj.declaredBacking",
+ substrs=["ran `frame variable obj._customDeclaredIvar`", "(int) 42"],
+ )
+ self.expect(
+ "dwim-print obj.undeclaredBacking",
+ substrs=["ran `frame variable obj._customUndeclaredIvar`", "(int) 7"],
+ )
+ self.expect(
+ "dwim-print obj.implicitBacking",
+ substrs=["ran `frame variable obj._implicitBacking`", "(int) 99"],
+ )
diff --git a/lldb/test/API/commands/dwim-print/objc/main.m b/lldb/test/API/commands/dwim-print/objc/main.m
index ac7efcf0f07abd..2695a77be060ac 100644
--- a/lldb/test/API/commands/dwim-print/objc/main.m
+++ b/lldb/test/API/commands/dwim-print/objc/main.m
@@ -14,11 +14,31 @@ @interface Parent : NSObject
@implementation Parent
@end
+ at interface C : NSObject {
+ int _customDeclaredIvar;
+}
+ at property int declaredBacking;
+ at property int undeclaredBacking;
+ at property int implicitBacking;
+ at end
+
+ at implementation C
+ at synthesize declaredBacking = _customDeclaredIvar;
+ at synthesize undeclaredBacking = _customUndeclaredIvar;
+// implicitBacking: no @synthesize -> compiler auto-creates _implicitBacking.
+ at end
+
int main(int argc, char **argv) {
Child *child = [Child new];
child.name = @"Seven";
Parent *parent = [Parent new];
parent.child = child;
puts("break here");
+
+ C *obj = [C new];
+ obj.declaredBacking = 42;
+ obj.undeclaredBacking = 7;
+ obj.implicitBacking = 99;
+ puts("break here for backing storage");
return 0;
}
>From 17ba9d4862a76fa34b8f4ba57e8f38a1c82ed8a1 Mon Sep 17 00:00:00 2001
From: Piotrek Jeremicz <piotrek at jeremicz.com>
Date: Mon, 21 Sep 2026 23:48:11 +0200
Subject: [PATCH 5/5] Removed unnecessary comments
---
lldb/include/lldb/Symbol/CompilerType.h | 1 -
lldb/include/lldb/Symbol/TypeSystem.h | 6 ------
lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 2 --
lldb/test/API/commands/dwim-print/objc/main.m | 1 -
4 files changed, 10 deletions(-)
diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index ffeacd49ffa69b..da22b35a619bf2 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -485,7 +485,6 @@ class CompilerType {
CompilerType GetDirectNestedTypeWithName(llvm::StringRef name) const;
- /// See TypeSystem::GetPropertyBackingStorageName.
llvm::StringRef
GetPropertyBackingStorageName(llvm::StringRef property_name) const;
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 3c141b9fc0dd3d..400bb0726a6274 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -403,12 +403,6 @@ class TypeSystem : public PluginInterface,
return CompilerType();
}
- /// Returns the name of the member that backs the named property on
- /// \a type, if this type system knows of one (e.g. an ivar reached via
- /// DW_AT_property_forward on a DWARF DW_TAG_property).
- ///
- /// \return An empty string if \a type has no property named
- /// \a property_name, or if that property has no known backing storage.
virtual llvm::StringRef
GetPropertyBackingStorageName(lldb::opaque_compiler_type_t type,
llvm::StringRef property_name) {
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 2f4b4a65433763..b27d69c1fb02ad 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -7097,8 +7097,6 @@ llvm::StringRef TypeSystemClang::GetPropertyBackingStorageName(
CompilerType compiler_type(weak_from_this(), type);
- // Callers will usually hand us an object pointer (e.g. `Foo *`); strip it
- // to get at the interface itself.
CompilerType class_type;
if (IsObjCObjectPointerType(compiler_type, &class_type))
compiler_type = class_type;
diff --git a/lldb/test/API/commands/dwim-print/objc/main.m b/lldb/test/API/commands/dwim-print/objc/main.m
index 2695a77be060ac..45d39d6a952d41 100644
--- a/lldb/test/API/commands/dwim-print/objc/main.m
+++ b/lldb/test/API/commands/dwim-print/objc/main.m
@@ -25,7 +25,6 @@ @interface C : NSObject {
@implementation C
@synthesize declaredBacking = _customDeclaredIvar;
@synthesize undeclaredBacking = _customUndeclaredIvar;
-// implicitBacking: no @synthesize -> compiler auto-creates _implicitBacking.
@end
int main(int argc, char **argv) {
More information about the lldb-commits
mailing list