Author: Zyn Date: 2026-04-08T01:26:41+04:00 New Revision: 103f821cb0977612fbff270df0e81801e8b93ed4
URL: https://github.com/llvm/llvm-project/commit/103f821cb0977612fbff270df0e81801e8b93ed4 DIFF: https://github.com/llvm/llvm-project/commit/103f821cb0977612fbff270df0e81801e8b93ed4.diff LOG: [lldb][DWARFASTParserClang] Handle pointer-to-member-data non-type (#189510) ## Reland Notes Re applying [187598](https://github.com/llvm/llvm-project/pull/187598) This is a reland of the original commit which was reverted due to a failure on the Windows buildbot. Root cause of the Windows failure: * The fix introduces TemplateArgument::Declaration (pointing to a FieldDecl) * GetValueParamType() in TypeSystemClang.cpp did not handle this kind, so CreateTemplateParameterList() created a TemplateTypeParmDecl instead of a NonTypeTemplateParmDecl for the corresponding template parameter. * On Windows, the Microsoft name mangler calls cast<NonTypeTemplateParmDecl>(Parm) when mangling member data pointer NTTPs, which crashed because Parm was a TemplateTypeParmDecl. * The Itanium mangler (Linux/Mac) does not inspect the parameter declaration, so the bug was latent there. Fix: * Added TemplateArgument::Declaration to GetValueParamType() so it returns argument.getParamTypeForDecl(), causing CreateTemplateParameterList() to create the correct NonTypeTemplateParmDecl. * Removed the @skipIfWindows decorator from the test. ## Description ### Problem MakeAPValue in DWARFASTParserClang.cpp did not handle pointer-to-member-data non-type template parameters (e.g., template <int S::*P>), causing LLDB to produce incorrect results or crash. DWARF encodes pointer-to-member-data NTTPs as `DW_TAG_template_value_parameter` with a `DW_AT_const_value` representing the byte offset of the member within the containing struct. MakeAPValue is responsible for converting this value into a clang APValue, but it only handled integer/enum and floating-point types. For pointer-to-member types, it returned `std::nullopt`. This caused the caller (ParseTemplateDIE) to fall back to creating a type-only TemplateArgument (kind=Type) instead of a value-carrying one. When two specializations differ only by which member they point to (e.g., MemberData<&S::x> / MemberData<&S::y>), both produce identical TemplateArguments. Clang's [findSpecialization](https://github.com/llvm/llvm-project/blob/3bc216c29cb42c7d94b617943b1d44afce605588/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp#L1674-L1677) then treats the second as a duplicate, so only one specialization exists in the AST. The second variable becomes unresolvable. (See Debugger Evidence section below) In more complex cases, this triggers an assertion failure in [clang::CXXRecordDecl::setBases(): cast()](https://github.com/llvm/llvm-project/blob/3bc216c29cb42c7d94b617943b1d44afce605588/clang/lib/AST/DeclCXX.cpp#L219) argument of incompatible type. ## Fix MakeAPValue: Added `IsMemberDataPointerType()` to the integral type check so that pointer-to-member byte offsets produce distinct APValues. Also replaced the silent return `std::nullopt` for unsupported types with `lldbassert` so unknown type classes are caught during development. `ResolveMemberDataPointerToFieldDecl`: New method that follows the DWARF chain to resolve the byte offset to the actual FieldDecl, creating TemplateArgument(Declaration) matching clang's own AST: DW_TAG_template_value_parameter (DW_AT_type) → DW_TAG_ptr_to_member_type (DW_AT_containing_type) → DW_TAG_structure_type → match DW_TAG_member by byte offset If resolution fails at any step, falls through to the integer APValue path as a safe fallback. Verified by comparing clang's AST (clang -Xclang -ast-dump) with LLDB's reconstructed AST (image dump ast) — both now produce TemplateArgument decl '&S::x' referencing the correct FieldDecl. Added: lldb/test/API/lang/cpp/non-type-template-param-member-ptr/Makefile lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py lldb/test/API/lang/cpp/non-type-template-param-member-ptr/main.cpp Modified: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 21288cad72ee4..fa76260dca63d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2008,28 +2008,94 @@ class DWARFASTParserClang::DelayedAddObjCClassProperty { static std::optional<clang::APValue> MakeAPValue(const clang::ASTContext &ast, CompilerType clang_type, - uint64_t value) { + uint64_t value, + const DWARFDIE &die) { std::optional<uint64_t> bit_width = llvm::expectedToOptional(clang_type.GetBitSize(nullptr)); if (!bit_width) return std::nullopt; bool is_signed = false; - const bool is_integral = clang_type.IsIntegerOrEnumerationType(is_signed); - llvm::APSInt apint(*bit_width, !is_signed); - apint = value; - - if (is_integral) + if (clang_type.IsIntegerOrEnumerationType(is_signed) || + clang_type.IsMemberDataPointerType()) { + llvm::APSInt apint(*bit_width, !is_signed); + apint = value; return clang::APValue(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; + if (clang_type.IsRealFloatingPointType()) { + llvm::APInt apint(*bit_width, value); + return clang::APValue(llvm::APFloat( + ast.getFloatTypeSemantics(ClangUtil::GetQualType(clang_type)), apint)); + } + + die.GetDWARF()->GetObjectFile()->GetModule()->ReportError( + "error: unsupported template value type in die {0:x16}, " + "please file a bug", + die.GetOffset()); + lldbassert(false && "Unsupported type for non-type template parameter"); + + return std::nullopt; +} + +clang::FieldDecl *DWARFASTParserClang::ResolveMemberDataPointerToFieldDecl( + const DWARFDIE &die, uint64_t member_byte_offset) { + Log *log = GetLog(DWARFLog::TypeCompletion); + + DWARFDIE type_die = die.GetReferencedDIE(DW_AT_type); + assert(type_die && type_die.Tag() == DW_TAG_ptr_to_member_type && + "DW_AT_type of a member data pointer must be " + "DW_TAG_ptr_to_member_type"); + + DWARFDIE containing_die = type_die.GetReferencedDIE(DW_AT_containing_type); + if (!containing_die) { + LLDB_LOG(log, + "ResolveMemberDataPointerToFieldDecl: DIE {0:x16} — " + "DW_TAG_ptr_to_member_type {1:x16} has no DW_AT_containing_type", + die.GetOffset(), type_die.GetOffset()); + return nullptr; + } + + Type *containing_type = die.ResolveTypeUID(containing_die); + if (!containing_type) { + LLDB_LOG(log, + "ResolveMemberDataPointerToFieldDecl: DIE {0:x16} — " + "failed to resolve containing type {1:x16}", + die.GetOffset(), containing_die.GetOffset()); + return nullptr; + } + + CompilerType containing_ct = containing_type->GetFullCompilerType(); + auto *record_decl = + m_ast.GetAsCXXRecordDecl(containing_ct.GetOpaqueQualType()); + if (!record_decl) { + LLDB_LOG(log, + "ResolveMemberDataPointerToFieldDecl: DIE {0:x16} — " + "containing type {1:x16} is not a CXXRecordDecl", + die.GetOffset(), containing_die.GetOffset()); + return nullptr; + } - return clang::APValue(llvm::APFloat( - ast.getFloatTypeSemantics(ClangUtil::GetQualType(clang_type)), apint)); + clang::ASTContext &ast = m_ast.getASTContext(); + for (auto *field : record_decl->fields()) { + if (ast.getFieldOffset(field) / 8 == member_byte_offset) { + LLDB_LOG(log, + "ResolveMemberDataPointerToFieldDecl: DIE {0:x16} — " + "resolved to field '{1}' at byte offset {2} in {3}", + die.GetOffset(), field->getName(), member_byte_offset, + containing_die.GetName()); + return field; + } + } + + LLDB_LOG(log, + "ResolveMemberDataPointerToFieldDecl: DIE {0:x16} — " + "no field found at byte offset {1} in {2}", + die.GetOffset(), member_byte_offset, containing_die.GetName()); + return nullptr; } bool DWARFASTParserClang::ParseTemplateDIE( @@ -2114,7 +2180,24 @@ bool DWARFASTParserClang::ParseTemplateDIE( name = nullptr; if (tag == DW_TAG_template_value_parameter && uval64_valid) { - if (auto value = MakeAPValue(ast, clang_type, uval64)) { + if (auto value = MakeAPValue(ast, clang_type, uval64, die)) { + // For pointer-to-member types, try to resolve to the actual FieldDecl + if (clang_type.IsMemberDataPointerType()) { + if (auto *field = + ResolveMemberDataPointerToFieldDecl(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 + die.GetDWARF()->GetObjectFile()->GetModule()->ReportError( + "error: failed to resolve member data pointer to FieldDecl " + "in die {0:x16}, please file a bug", + die.GetOffset()); + } + 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..ca76bcdc4ace2 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-data-member, follow the DWARF chain to find the FieldDecl + /// at the given byte offset within the containing class. + clang::FieldDecl *ResolveMemberDataPointerToFieldDecl( + 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/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index fba6aec296324..86d39b51e9b4c 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -1345,6 +1345,8 @@ QualType GetValueParamType(const clang::TemplateArgument &argument) { return argument.getIntegralType(); case TemplateArgument::StructuralValue: return argument.getStructuralValueType(); + case TemplateArgument::Declaration: + return argument.getParamTypeForDecl(); default: return {}; } 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..2133c58aeb8a5 --- /dev/null +++ b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/TestCppNonTypeTemplateParamPtrToMember.py @@ -0,0 +1,14 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test_member_data_pointer(self): + """Member data pointer NTTPs: MemberData<&S::x> vs MemberData<&S::y>""" + self.build() + self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + # Both must be resolvable as distinct specializations. + self.expect_expr("md1", result_type="MemberData<&S::x>") + self.expect_expr("md2", result_type="MemberData<&S::y>") 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..3de9c024c82f9 --- /dev/null +++ b/lldb/test/API/lang/cpp/non-type-template-param-member-ptr/main.cpp @@ -0,0 +1,16 @@ +struct S { + int x; + int y; +}; + +// --- Member data pointer NTTP --- +template <int S::*P> struct MemberData { + int get(S &s) { return s.*P; } +}; +MemberData<&S::x> md1; +MemberData<&S::y> md2; + +int main() { + S s{1, 2}; + return md1.get(s) + md2.get(s); +} _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
