[Lldb-commits] [lldb] [lldb][DWARF] Look up static data member constants in enclosing type (PR #236041)
Roland McGrath via lldb-commits
lldb-commits at lists.llvm.org
Mon Nov 2 15:49:31 PST 2026
https://github.com/frobtech updated https://github.com/llvm/llvm-project/pull/236041
>From 8036c4c73199314159fb3887c4cb3708038d6520 Mon Sep 17 00:00:00 2001
From: Roland McGrath <mcgrathr at google.com>
Date: Tue, 27 Oct 2026 02:11:17 -0700
Subject: [PATCH 1/3] [lldb][DWARF] Look up static data member constants in
enclosing type
Constant-initialized static data members without out-of-line definitions
are emitted as in-class declaration DIEs (DW_TAG_variable in DWARF v5 or
DW_TAG_member in DWARF v4 and earlier) with DW_AT_const_value and no
DW_AT_location. Since 4db54e659763 (#74580), Clang does not emit
artificial out-of-line DW_TAG_variable definitions for these constants,
and DWARF accelerator tables (.debug_names and .apple_names) do not
index in-class member declarations as global variables. As a result,
SymbolFileDWARF::FindGlobalVariables could only find them when using
ManualDWARFIndex.
When FindGlobalVariables is given a scoped name (such as A::int_val or
ns::Foo::mem) and finds no matches in the global variables index, look
up the enclosing struct/class/union type via GetTypesWithQuery (with the
existing -gsimple-template-names fallback) and inspect its child DIEs
for matching static data members with DW_AT_const_value or
DW_AT_location.
Part of #165079
Assisted-by: Gemini
---
.../SymbolFile/DWARF/SymbolFileDWARF.cpp | 111 ++++++++----
.../TestConstStaticIntegralMember.py | 14 +-
.../DWARF/x86/find-basic-variable.cpp | 11 ++
.../DWARF/DWARFDebugNamesIndexTest.cpp | 166 ++++++++++++++++++
4 files changed, 262 insertions(+), 40 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 41e3372584d4f4..f5bc9d5843a362 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -2318,6 +2318,36 @@ bool SymbolFileDWARF::DeclContextMatchesThisSymbolFile(
return false;
}
+/// Split a name up into a basename and template parameters.
+static bool SplitTemplateParams(llvm::StringRef fullname,
+ llvm::StringRef &basename,
+ llvm::StringRef &template_params) {
+ auto it = fullname.find('<');
+ if (it == llvm::StringRef::npos) {
+ basename = fullname;
+ template_params = llvm::StringRef();
+ return false;
+ }
+ basename = fullname.slice(0, it);
+ template_params = fullname.slice(it, fullname.size());
+ return true;
+}
+
+static bool UpdateCompilerContextForSimpleTemplateNames(TypeQuery &match) {
+ // We need to find any names in the context that have template parameters
+ // and strip them so the context can be matched when -gsimple-template-names
+ // is being used. Returns true if any of the context items were updated.
+ bool any_context_updated = false;
+ for (auto &context : match.GetContextRef()) {
+ llvm::StringRef basename, params;
+ if (SplitTemplateParams(context.name.GetStringRef(), basename, params)) {
+ context.name = ConstString(basename);
+ any_context_updated = true;
+ }
+ }
+ return any_context_updated;
+}
+
void SymbolFileDWARF::FindGlobalVariables(
ConstString name, const CompilerDeclContext &parent_decl_ctx,
uint32_t max_matches, VariableList &variables) {
@@ -2359,7 +2389,7 @@ void SymbolFileDWARF::FindGlobalVariables(
uint32_t pruned_idx = original_size;
SymbolContext sc;
- m_index->GetGlobalVariables(ConstString(basename), [&](DWARFDIE die) {
+ auto process_die = [&](DWARFDIE die) {
if (!sc.module_sp)
sc.module_sp = m_objfile_sp->GetModule();
assert(sc.module_sp);
@@ -2402,7 +2432,54 @@ void SymbolFileDWARF::FindGlobalVariables(
return IterationAction::Continue;
return IterationAction::Stop;
- });
+ };
+
+ m_index->GetGlobalVariables(ConstString(basename), process_die);
+
+ // Constant-initialized static data members without out-of-line definitions
+ // are emitted as in-class declaration DIEs with DW_AT_const_value and are
+ // not indexed in accelerator tables (.debug_names or .apple_names). If
+ // looking up a scoped name found no global variables, look up the enclosing
+ // record type and check its children for matching static data members.
+ if (!context.empty() && variables.GetSize() == original_size) {
+ auto process_type_die = [&](DWARFDIE type_die) {
+ if (!type_die.IsStructUnionOrClass())
+ return IterationAction::Continue;
+ for (DWARFDIE child_die : type_die.children()) {
+ const dw_tag_t child_tag = child_die.Tag();
+ if (child_tag != DW_TAG_variable && child_tag != DW_TAG_member)
+ continue;
+ if (child_tag == DW_TAG_member &&
+ !child_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0))
+ continue;
+ if (llvm::StringRef(child_die.GetName()) != basename)
+ continue;
+ DWARFFormValue form_value;
+ if (!child_die.GetDIE()->GetAttributeValue(
+ child_die.GetCU(), DW_AT_const_value, form_value) &&
+ !child_die.GetDIE()->GetAttributeValue(child_die.GetCU(),
+ DW_AT_location, form_value))
+ continue;
+ if (process_die(child_die) == IterationAction::Stop)
+ return IterationAction::Stop;
+ }
+ return IterationAction::Continue;
+ };
+
+ TypeQuery query(context);
+ m_index->GetTypesWithQuery(query, process_type_die);
+ if (variables.GetSize() == original_size) {
+ TypeQuery query_simple(query);
+ if (UpdateCompilerContextForSimpleTemplateNames(query_simple)) {
+ m_index->GetTypesWithQuery(query_simple, [&](DWARFDIE die) {
+ if (query.ContextMatches(
+ die.GetTypeLookupContext(/*derive_template_names=*/true)))
+ return process_type_die(die);
+ return IterationAction::Continue;
+ });
+ }
+ }
+ }
// Return the number of variable that were appended to the list
const uint32_t num_matches = variables.GetSize() - original_size;
@@ -2814,36 +2891,6 @@ void SymbolFileDWARF::GetMangledNamesForFunction(
}
}
-/// Split a name up into a basename and template parameters.
-static bool SplitTemplateParams(llvm::StringRef fullname,
- llvm::StringRef &basename,
- llvm::StringRef &template_params) {
- auto it = fullname.find('<');
- if (it == llvm::StringRef::npos) {
- basename = fullname;
- template_params = llvm::StringRef();
- return false;
- }
- basename = fullname.slice(0, it);
- template_params = fullname.slice(it, fullname.size());
- return true;
-}
-
-static bool UpdateCompilerContextForSimpleTemplateNames(TypeQuery &match) {
- // We need to find any names in the context that have template parameters
- // and strip them so the context can be matched when -gsimple-template-names
- // is being used. Returns true if any of the context items were updated.
- bool any_context_updated = false;
- for (auto &context : match.GetContextRef()) {
- llvm::StringRef basename, params;
- if (SplitTemplateParams(context.name.GetStringRef(), basename, params)) {
- context.name = ConstString(basename);
- any_context_updated = true;
- }
- }
- return any_context_updated;
-}
-
uint64_t SymbolFileDWARF::GetDebugInfoSize(bool load_all_debug_info) {
DWARFDebugInfo &info = DebugInfo();
uint32_t num_comp_units = info.GetNumUnits();
diff --git a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
index 7a05a5854986cb..630e956280609e 100644
--- a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
+++ b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
@@ -148,14 +148,16 @@ def check_inline_static_members(self, flags):
# Fails on Windows for unknown reasons.
@skipIfWindows
- # On linux this passes due to the manual index
- @expectedFailureDarwin(debug_info=no_match(["dsym"]))
@skipIf(debug_info=["dsym"], compiler=["clang"], compiler_version=["<", "19.0"])
def test_inline_static_members_dwarf5(self):
self.check_inline_static_members("-gdwarf-5")
- # On linux this passes due to the manual index
- @expectedFailureDarwin
+ # dsymutil strips the debug info for classes that only have const static
+ # data members without locations in DWARF v4 and earlier.
+ @expectedFailureDarwin(
+ debug_info=["dsym"],
+ bugnumber="https://github.com/llvm/llvm-project/issues/165079",
+ )
def test_inline_static_members_dwarf4(self):
self.check_inline_static_members("-gdwarf-4")
@@ -202,14 +204,10 @@ def check_shadowed_static_inline_members(self, flags):
# Fails on Windows for unknown reasons.
@skipIfWindows
- # On linux this passes due to the manual index
- @expectedFailureDarwin(debug_info=no_match(["dsym"]))
@skipIf(debug_info=["dsym"], compiler=["clang"], compiler_version=["<", "19.0"])
def test_shadowed_static_inline_members_dwarf5(self):
self.check_shadowed_static_inline_members("-gdwarf-5")
- # On linux this passes due to the manual index
- @expectedFailureDarwin
def test_shadowed_static_inline_members_dwarf4(self):
self.check_shadowed_static_inline_members("-gdwarf-4")
diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/find-basic-variable.cpp b/lldb/test/Shell/SymbolFile/DWARF/x86/find-basic-variable.cpp
index b6e2252c284025..0d97265a0da2e5 100644
--- a/lldb/test/Shell/SymbolFile/DWARF/x86/find-basic-variable.cpp
+++ b/lldb/test/Shell/SymbolFile/DWARF/x86/find-basic-variable.cpp
@@ -10,6 +10,8 @@
// RUN: FileCheck --check-prefix=REGEX %s
// RUN: lldb-test symbols --name=not_there --find=variable %t | \
// RUN: FileCheck --check-prefix=EMPTY %s
+// RUN: lldb-test symbols --name=sbar::cbar --find=variable %t | \
+// RUN: FileCheck --check-prefix=STATIC_CONST %s
//
// RUN: %clangxx %s -g -c -o %t --target=x86_64-apple-macosx
// RUN: lldb-test symbols --name=foo --find=variable --context=context %t | \
@@ -20,6 +22,8 @@
// RUN: FileCheck --check-prefix=REGEX %s
// RUN: lldb-test symbols --name=not_there --find=variable %t | \
// RUN: FileCheck --check-prefix=EMPTY %s
+// RUN: lldb-test symbols --name=sbar::cbar --find=variable %t | \
+// RUN: FileCheck --check-prefix=STATIC_CONST %s
//
// RUN: %clangxx %s -g -c -o %t.o --target=x86_64-pc-linux -gdwarf-5 -gpubnames
// RUN: ld.lld %t.o -o %t
@@ -32,11 +36,15 @@
// RUN: FileCheck --check-prefix=REGEX %s
// RUN: lldb-test symbols --name=not_there --find=variable %t | \
// RUN: FileCheck --check-prefix=EMPTY %s
+// RUN: lldb-test symbols --name=sbar::cbar --find=variable %t | \
+// RUN: FileCheck --check-prefix=STATIC_CONST %s
/// Test a per-module index built by lld.
// RUN: ld.lld --debug-names %t.o -o %t
// RUN: lldb-test symbols --name=foo --find=variable --context=context %t | \
// RUN: FileCheck --check-prefix=CONTEXT %s
+// RUN: lldb-test symbols --name=sbar::cbar --find=variable %t | \
+// RUN: FileCheck --check-prefix=STATIC_CONST %s
// NAMES: Name: .debug_names
@@ -44,6 +52,7 @@
// NAME: Found 4 variables:
// CONTEXT: Found 1 variables:
// REGEX: Found 5 variables:
+// STATIC_CONST: Found 1 variables:
int foo;
// NAME-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]]
// REGEX-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-2]]
@@ -64,6 +73,8 @@ struct sbar {
static int foo;
// NAME-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]]
// REGEX-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-2]]
+ static const int cbar = 42;
+// STATIC_CONST-DAG: name = "cbar", type = {{.*}} (const int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]]
};
int sbar::foo;
diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFDebugNamesIndexTest.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFDebugNamesIndexTest.cpp
index dd8a0742f6b70c..0fec6a182ad084 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFDebugNamesIndexTest.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFDebugNamesIndexTest.cpp
@@ -11,6 +11,8 @@
#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
#include "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h"
#include "TestingSupport/Symbol/YAMLModuleTester.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
#include "lldb/lldb-private-enumerations.h"
#include "llvm/ADT/STLExtras.h"
#include "gmock/gmock.h"
@@ -207,3 +209,167 @@ TEST(DWARFDebugNamesIndexTest, FullyQualifiedQueryWithoutIDXParent) {
check_num_matches(*index, 1, {make_entry("2"), make_entry("1")});
check_num_matches(*index, 1, {make_entry("2")});
}
+
+TEST(DWARFDebugNamesIndexTest, FindGlobalVariablesStaticDataMember) {
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+DWARF:
+ debug_str:
+ - 'int'
+ - 'A'
+ - 'int_val'
+ - 'ns'
+ - 'Foo'
+ - 'mem'
+ debug_abbrev:
+ - Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x2
+ Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_encoding
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Code: 0x3
+ Tag: DW_TAG_structure_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Code: 0x4
+ Tag: DW_TAG_variable
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Attribute: DW_AT_const_value
+ Form: DW_FORM_data4
+ - Code: 0x5
+ Tag: DW_TAG_namespace
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Code: 0x6
+ Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Attribute: DW_AT_const_value
+ Form: DW_FORM_data4
+ debug_info:
+ - Version: 5
+ UnitType: DW_UT_compile
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x0021 # DW_LANG_C_plus_plus_14
+ - AbbrCode: 0x2 # 0x0f: base_type "int"
+ Values:
+ - Value: 0x0 # "int"
+ - Value: 0x5 # DW_ATE_signed
+ - Value: 0x4
+ - AbbrCode: 0x3 # 0x16: structure_type "A"
+ Values:
+ - Value: 0x4 # "A"
+ - Value: 0x1
+ - AbbrCode: 0x4 # 0x1c: variable "int_val"
+ Values:
+ - Value: 0x6 # "int_val"
+ - Value: 0x0f # -> int
+ - Value: 0x1
+ - Value: 0x2a # 42
+ - AbbrCode: 0x0 # 0x29: end A
+ - AbbrCode: 0x5 # 0x2a: namespace "ns"
+ Values:
+ - Value: 0xe # "ns"
+ - AbbrCode: 0x3 # 0x2f: structure_type "Foo"
+ Values:
+ - Value: 0x11 # "Foo"
+ - Value: 0x1
+ - AbbrCode: 0x6 # 0x35: member "mem"
+ Values:
+ - Value: 0x15 # "mem"
+ - Value: 0x0f # -> int
+ - Value: 0x1
+ - Value: 0x0a # 10
+ - AbbrCode: 0x0 # 0x42: end Foo
+ - AbbrCode: 0x0 # 0x43: end ns
+ - AbbrCode: 0x0 # 0x44: end CU
+ debug_names:
+ Abbreviations:
+ - Code: 0x1
+ Tag: DW_TAG_structure_type
+ Indices:
+ - Idx: DW_IDX_die_offset
+ Form: DW_FORM_ref4
+ - Code: 0x2
+ Tag: DW_TAG_namespace
+ Indices:
+ - Idx: DW_IDX_die_offset
+ Form: DW_FORM_ref4
+ Entries:
+ - Name: 0x4 # "A"
+ Code: 0x1
+ Values:
+ - 0x16
+ - Name: 0xe # "ns"
+ Code: 0x2
+ Values:
+ - 0x2a
+ - Name: 0x11 # "Foo"
+ Code: 0x1
+ Values:
+ - 0x2f
+)";
+
+ YAMLModuleTester t(yamldata);
+ auto *symbol_file =
+ llvm::cast<SymbolFileDWARF>(t.GetModule()->GetSymbolFile());
+ auto *index = static_cast<DebugNamesDWARFIndex *>(symbol_file->getIndex());
+ ASSERT_NE(index, nullptr);
+
+ VariableList vars;
+ symbol_file->FindGlobalVariables(ConstString("A::int_val"),
+ CompilerDeclContext(), UINT32_MAX, vars);
+ ASSERT_EQ(vars.GetSize(), 1u);
+ VariableSP var_sp = vars.GetVariableAtIndex(0);
+ ASSERT_TRUE(var_sp);
+ EXPECT_EQ(var_sp->GetName(), ConstString("A::int_val"));
+ EXPECT_TRUE(var_sp->GetLocationIsConstantValueData());
+
+ VariableList ns_vars;
+ symbol_file->FindGlobalVariables(ConstString("ns::Foo::mem"),
+ CompilerDeclContext(), UINT32_MAX, ns_vars);
+ ASSERT_EQ(ns_vars.GetSize(), 1u);
+ VariableSP ns_var_sp = ns_vars.GetVariableAtIndex(0);
+ ASSERT_TRUE(ns_var_sp);
+ EXPECT_EQ(ns_var_sp->GetName(), ConstString("ns::Foo::mem"));
+ EXPECT_TRUE(ns_var_sp->GetLocationIsConstantValueData());
+}
>From 30722600a27de5ca2c3f4d29d1b3d657bc08c116 Mon Sep 17 00:00:00 2001
From: Roland McGrath <mcgrathr at google.com>
Date: Tue, 27 Oct 2026 02:21:41 -0700
Subject: [PATCH 2/3] [lldb][test] Format find-basic-variable.cpp
---
lldb/test/Shell/SymbolFile/DWARF/x86/find-basic-variable.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/find-basic-variable.cpp b/lldb/test/Shell/SymbolFile/DWARF/x86/find-basic-variable.cpp
index 0d97265a0da2e5..3faa9c03fd5b61 100644
--- a/lldb/test/Shell/SymbolFile/DWARF/x86/find-basic-variable.cpp
+++ b/lldb/test/Shell/SymbolFile/DWARF/x86/find-basic-variable.cpp
@@ -74,7 +74,7 @@ struct sbar {
// NAME-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]]
// REGEX-DAG: name = "foo", type = {{.*}} (int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-2]]
static const int cbar = 42;
-// STATIC_CONST-DAG: name = "cbar", type = {{.*}} (const int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]]
+ // STATIC_CONST-DAG: name = "cbar", type = {{.*}} (const int), {{.*}} decl = find-basic-variable.cpp:[[@LINE-1]]
};
int sbar::foo;
>From c281210014290c0077b3ec409b965e7c4027c208 Mon Sep 17 00:00:00 2001
From: Roland McGrath <mcgrathr at google.com>
Date: Sun, 1 Nov 2026 18:03:42 -0800
Subject: [PATCH 3/3] [lldb][test] Remove incorrect issue reference and comment
The bugnumber and explanatory comment added to
test_inline_static_members_dwarf4 cited an unrelated issue and an
unsupported explanation. Drop both.
Assisted-by: Gemini
---
.../TestConstStaticIntegralMember.py | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
index 630e956280609e..151be18f64c806 100644
--- a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
+++ b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
@@ -152,12 +152,7 @@ def check_inline_static_members(self, flags):
def test_inline_static_members_dwarf5(self):
self.check_inline_static_members("-gdwarf-5")
- # dsymutil strips the debug info for classes that only have const static
- # data members without locations in DWARF v4 and earlier.
- @expectedFailureDarwin(
- debug_info=["dsym"],
- bugnumber="https://github.com/llvm/llvm-project/issues/165079",
- )
+ @expectedFailureDarwin(debug_info=["dsym"])
def test_inline_static_members_dwarf4(self):
self.check_inline_static_members("-gdwarf-4")
More information about the lldb-commits
mailing list