[Lldb-commits] [lldb] [lldb][DWARFASTParserClang] Fix handle non-type template parameters in MakeAPValue (PR #186629)

via lldb-commits lldb-commits at lists.llvm.org
Sun Mar 15 16:47:03 PDT 2026


https://github.com/zyn-li updated https://github.com/llvm/llvm-project/pull/186629

>From 61e17a3af68f36536e98bb676a5fda541532296d Mon Sep 17 00:00:00 2001
From: Zhiyuan Li <zynli at meta.com>
Date: Sun, 15 Mar 2026 16:02:35 -0700
Subject: [PATCH 1/3] [lldb][TypeSystem] Add
 CompilerType::IsMemberDataPointerType

---
 lldb/include/lldb/Symbol/CompilerType.h       |  2 ++
 lldb/include/lldb/Symbol/TypeSystem.h         |  2 ++
 .../TypeSystem/Clang/TypeSystemClang.cpp      |  9 ++++++
 .../TypeSystem/Clang/TypeSystemClang.h        |  2 ++
 lldb/source/Symbol/CompilerType.cpp           |  7 +++++
 lldb/unittests/Symbol/TestTypeSystemClang.cpp | 29 +++++++++++++++++++
 6 files changed, 51 insertions(+)

diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index 5f152b6f26a89..ca310c9098841 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -166,6 +166,8 @@ class CompilerType {
 
   bool IsMemberFunctionPointerType() const;
 
+  bool IsMemberDataPointerType() const;
+
   bool
   IsBlockPointerType(CompilerType *function_pointer_type_ptr = nullptr) const;
 
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 8ee7df40051bc..d68abba0b384d 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -178,6 +178,8 @@ class TypeSystem : public PluginInterface,
   virtual bool
   IsMemberFunctionPointerType(lldb::opaque_compiler_type_t type) = 0;
 
+  virtual bool IsMemberDataPointerType(lldb::opaque_compiler_type_t type) = 0;
+
   virtual bool IsBlockPointerType(lldb::opaque_compiler_type_t type,
                                   CompilerType *function_pointer_type_ptr) = 0;
 
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 211630f24c7f4..bf7666cd9d5e0 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -3169,6 +3169,15 @@ bool TypeSystemClang::IsMemberFunctionPointerType(
   return IsTypeImpl(type, isMemberFunctionPointerType);
 }
 
+bool TypeSystemClang::IsMemberDataPointerType(
+    lldb::opaque_compiler_type_t type) {
+  auto isMemberDataPointerType = [](clang::QualType qual_type) {
+    return qual_type->isMemberDataPointerType();
+  };
+
+  return IsTypeImpl(type, isMemberDataPointerType);
+}
+
 bool TypeSystemClang::IsFunctionPointerType(lldb::opaque_compiler_type_t type) {
   auto isFunctionPointerType = [](clang::QualType qual_type) {
     return qual_type->isFunctionPointerType();
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index e2c63741c82f8..d6c221906a997 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -672,6 +672,8 @@ class TypeSystemClang : public TypeSystem {
 
   bool IsMemberFunctionPointerType(lldb::opaque_compiler_type_t type) override;
 
+  bool IsMemberDataPointerType(lldb::opaque_compiler_type_t type) override;
+
   bool IsBlockPointerType(lldb::opaque_compiler_type_t type,
                           CompilerType *function_pointer_type_ptr) override;
 
diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp
index a568923e83201..20a69becdd56f 100644
--- a/lldb/source/Symbol/CompilerType.cpp
+++ b/lldb/source/Symbol/CompilerType.cpp
@@ -177,6 +177,13 @@ bool CompilerType::IsMemberFunctionPointerType() const {
   return false;
 }
 
+bool CompilerType::IsMemberDataPointerType() const {
+  if (IsValid())
+    if (auto type_system_sp = GetTypeSystem())
+      return type_system_sp->IsMemberDataPointerType(m_type);
+  return false;
+}
+
 bool CompilerType::IsBlockPointerType(
     CompilerType *function_pointer_type_ptr) const {
   if (IsValid())
diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
index d8d95f17e5104..3946c8c737870 100644
--- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp
+++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
@@ -1518,3 +1518,32 @@ TEST_P(TestTypeSystemClangAsmLabel, DeclGetMangledName) {
 
 INSTANTIATE_TEST_SUITE_P(AsmLabelTests, TestTypeSystemClangAsmLabel,
                          testing::ValuesIn(g_asm_label_test_cases));
+
+TEST_F(TestTypeSystemClang, TestIsMemberDataPointerType) {
+  // Create struct S { int x; void foo(); };
+  CompilerType int_type = m_ast->GetBasicType(lldb::eBasicTypeInt);
+  CompilerType record_type = clang_utils::createRecord(*m_ast, "S");
+
+  // int S::* — member data pointer
+  CompilerType member_data_ptr =
+      TypeSystemClang::CreateMemberPointerType(record_type, int_type);
+  EXPECT_TRUE(member_data_ptr.IsMemberDataPointerType());
+  EXPECT_FALSE(member_data_ptr.IsMemberFunctionPointerType());
+
+  // void (S::*)() — member function pointer
+  CompilerType void_type = m_ast->GetBasicType(lldb::eBasicTypeVoid);
+  CompilerType func_type = m_ast->CreateFunctionType(void_type, {}, false, 0U);
+  CompilerType member_func_ptr =
+      TypeSystemClang::CreateMemberPointerType(record_type, func_type);
+  EXPECT_FALSE(member_func_ptr.IsMemberDataPointerType());
+  EXPECT_TRUE(member_func_ptr.IsMemberFunctionPointerType());
+
+  // int* — regular pointer, neither member data nor member function
+  CompilerType regular_ptr = int_type.GetPointerType();
+  EXPECT_FALSE(regular_ptr.IsMemberDataPointerType());
+  EXPECT_FALSE(regular_ptr.IsMemberFunctionPointerType());
+
+  // int — not a pointer at all
+  EXPECT_FALSE(int_type.IsMemberDataPointerType());
+  EXPECT_FALSE(int_type.IsMemberFunctionPointerType());
+}

>From a406162bd33e687c93605075a4d964c8c3b8e4ff Mon Sep 17 00:00:00 2001
From: Zhiyuan Li <zynli at meta.com>
Date: Fri, 13 Mar 2026 18:05:10 -0700
Subject: [PATCH 2/3] [lldb][DWARFASTParserClang] Handle pointer-to-member
 non-type template parameters in MakeAPValue

---
 .../SymbolFile/DWARF/DWARFASTParserClang.cpp  | 22 ++++++++++----
 .../Makefile                                  |  3 ++
 .../TestCppNonTypeTemplateParamPtrToMember.py | 23 ++++++++++++++
 .../main.cpp                                  | 30 +++++++++++++++++++
 4 files changed, 73 insertions(+), 5 deletions(-)
 create mode 100644 lldb/test/API/lang/cpp/non-type-template-param-member-ptr/Makefile
 create mode 100644 lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py
 create mode 100644 lldb/test/API/lang/cpp/non-type-template-param-member-ptr/main.cpp

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index cb33fc21bfba9..7f4b2ad177efb 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -2015,7 +2015,11 @@ static std::optional<clang::APValue> MakeAPValue(const clang::ASTContext &ast,
     return std::nullopt;
 
   bool is_signed = false;
-  const bool is_integral = clang_type.IsIntegerOrEnumerationType(is_signed);
+  const bool is_integral = clang_type.IsIntegerOrEnumerationType(is_signed) ||
+                           clang_type.IsPointerOrReferenceType() ||
+                           clang_type.IsMemberFunctionPointerType() ||
+                           clang_type.IsMemberDataPointerType() ||
+                           clang_type.IsNullPtrType();
 
   llvm::APSInt apint(*bit_width, !is_signed);
   apint = value;
@@ -2023,13 +2027,21 @@ static std::optional<clang::APValue> MakeAPValue(const clang::ASTContext &ast,
   if (is_integral)
     return clang::APValue(apint);
 
+  if (clang_type.IsRealFloatingPointType()) {
+    return clang::APValue(llvm::APFloat(
+        ast.getFloatTypeSemantics(ClangUtil::GetQualType(clang_type)), apint));
+  }
+
   // FIXME: we currently support a limited set of floating point types.
   // E.g., 16-bit floats are not supported.
-  if (!clang_type.IsRealFloatingPointType())
-    return std::nullopt;
 
-  return clang::APValue(llvm::APFloat(
-      ast.getFloatTypeSemantics(ClangUtil::GetQualType(clang_type)), apint));
+  LLDB_LOG(GetLog(LLDBLog::Types),
+           "MakeAPValue: Unsupported NTTP type class: {0}",
+           clang_type.GetTypeClass());
+
+  lldbassert(false && "Unsupported type for non-type template parameter");
+
+  return std::nullopt;
 }
 
 bool DWARFASTParserClang::ParseTemplateDIE(
diff --git a/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/Makefile b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py
new file mode 100644
index 0000000000000..7af6d9014b26d
--- /dev/null
+++ b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py
@@ -0,0 +1,23 @@
+"""
+Test that LLDB correctly distinguishes template specializations
+with different pointer-to-member non-type template parameter values.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+    @no_debug_info_test
+    def test(self):
+        self.build()
+        self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+
+        # Both Data<&S::x> and Data<&S::y> should be resolvable as
+        # distinct specializations. Without the fix, the second
+        # specialization is rejected as a duplicate and dy is
+        # unresolvable.
+        self.expect_expr("dx", result_type="Data<0>")
+        self.expect_expr("dy", result_type="Data<4>")
diff --git a/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/main.cpp b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/main.cpp
new file mode 100644
index 0000000000000..0a236ce3604bb
--- /dev/null
+++ b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/main.cpp
@@ -0,0 +1,30 @@
+// Test that LLDB correctly distinguishes template specializations with
+// different pointer-to-member non-type template parameter values.
+//
+// When a template takes a pointer-to-member as a non-type parameter
+// (e.g., template <int S::*P>), DWARF encodes each instantiation's
+// member as a DW_AT_const_value (the byte offset of the member).
+// LLDB must treat these as distinct values so that each specialization
+// (e.g., Data<&S::x> vs Data<&S::y>) gets its own type in the AST.
+//
+// Without the fix, MakeAPValue rejects pointer-to-member types,
+// causing both specializations to produce identical TemplateArguments.
+// findSpecialization then treats the second as a duplicate, so only
+// one specialization exists and the other variable becomes unresolvable.
+
+struct S {
+  int x;
+  int y;
+};
+
+template <int S::*P> struct Data {
+  int get(S &s) { return s.*P; }
+};
+
+Data<&S::x> dx;
+Data<&S::y> dy;
+
+int main() {
+  S s{1, 2};
+  return dx.get(s) + dy.get(s);
+}

>From efadeabbd3d6ec9ac866c6f087f3b28a3ad1be5e Mon Sep 17 00:00:00 2001
From: Zhiyuan Li <zynli at meta.com>
Date: Sun, 15 Mar 2026 15:35:41 -0700
Subject: [PATCH 3/3] [lldb][DWARFASTParserClang] Resolve pointer-to-member
 NTTP values to FieldDecls

---
 .../SymbolFile/DWARF/DWARFASTParserClang.cpp  | 46 +++++++++++++++++++
 .../SymbolFile/DWARF/DWARFASTParserClang.h    |  7 +++
 .../TestCppNonTypeTemplateParamPtrToMember.py |  4 +-
 3 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 7f4b2ad177efb..2a6acf32e3ebf 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -2044,6 +2044,39 @@ static std::optional<clang::APValue> MakeAPValue(const clang::ASTContext &ast,
   return std::nullopt;
 }
 
+clang::FieldDecl *DWARFASTParserClang::ResolveFieldFromPtrToMemberValue(
+    const DWARFDIE &die, uint64_t member_byte_offset) {
+  // die (DW_AT_type) → DW_TAG_ptr_to_member_type
+  DWARFDIE type_die = die.GetReferencedDIE(DW_AT_type);
+  if (!type_die || type_die.Tag() != DW_TAG_ptr_to_member_type)
+    return nullptr;
+
+  // → DW_AT_containing_type → struct/class DIE
+  DWARFDIE containing_die = type_die.GetReferencedDIE(DW_AT_containing_type);
+  if (!containing_die)
+    return nullptr;
+
+  // Resolve and complete the containing class
+  Type *containing_type = die.ResolveTypeUID(containing_die);
+  if (!containing_type)
+    return nullptr;
+
+  CompilerType containing_ct = containing_type->GetFullCompilerType();
+  auto *record_decl =
+      m_ast.GetAsCXXRecordDecl(containing_ct.GetOpaqueQualType());
+  if (!record_decl)
+    return nullptr;
+
+  // Walk fields, match by byte offset
+  clang::ASTContext &ast = m_ast.getASTContext();
+  for (auto *field : record_decl->fields()) {
+    if (ast.getFieldOffset(field) / 8 == member_byte_offset)
+      return field;
+  }
+
+  return nullptr;
+}
+
 bool DWARFASTParserClang::ParseTemplateDIE(
     const DWARFDIE &die,
     TypeSystemClang::TemplateParameterInfos &template_param_infos) {
@@ -2127,6 +2160,19 @@ bool DWARFASTParserClang::ParseTemplateDIE(
 
       if (tag == DW_TAG_template_value_parameter && uval64_valid) {
         if (auto value = MakeAPValue(ast, clang_type, uval64)) {
+          // For pointer-to-member types, try to resolve to the actual
+          // FieldDecl so it displays as "&S::x" instead of "0".
+          if (clang_type.IsMemberDataPointerType()) {
+            if (auto *field = ResolveFieldFromPtrToMemberValue(die, uval64)) {
+                template_param_infos.InsertArg(
+                    name, clang::TemplateArgument(
+                              field, ClangUtil::GetQualType(clang_type),
+                              is_default_template_arg));
+                return true;
+            }
+            // Failed to resolve FieldDecl, fall through to integer path
+          }
+
           template_param_infos.InsertArg(
               name, clang::TemplateArgument(
                         ast, ClangUtil::GetQualType(clang_type),
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
index 03c431c73fb6f..f43638b005459 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -185,6 +185,13 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
                         lldb_private::TypeSystemClang::TemplateParameterInfos
                             &template_param_infos);
 
+  /// Given a DW_TAG_template_value_parameter DIE whose type is a
+  /// pointer-to-member, follow the DWARF chain to find the FieldDecl
+  /// at the given byte offset within the containing class.
+  clang::FieldDecl *ResolveFieldFromPtrToMemberValue(
+      const lldb_private::plugin::dwarf::DWARFDIE &die,
+      uint64_t member_byte_offset);
+
   bool ParseTemplateParameterInfos(
       const lldb_private::plugin::dwarf::DWARFDIE &parent_die,
       lldb_private::TypeSystemClang::TemplateParameterInfos
diff --git a/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py
index 7af6d9014b26d..5d98ca21fb17b 100644
--- a/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py
+++ b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py
@@ -19,5 +19,5 @@ def test(self):
         # distinct specializations. Without the fix, the second
         # specialization is rejected as a duplicate and dy is
         # unresolvable.
-        self.expect_expr("dx", result_type="Data<0>")
-        self.expect_expr("dy", result_type="Data<4>")
+        self.expect_expr("dx", result_type="Data<&S::x>")
+        self.expect_expr("dy", result_type="Data<&S::y>")



More information about the lldb-commits mailing list