[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