[lldb][DWARFASTParserClang] Handle pointer-to-member-data non-type template (#187598) ## 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. ## Test Plan Added `lldb/test/API/lang/cpp/non-type-template-param-member-ptr/` with a test that creates two specializations (MemberData<&S::x> and MemberData<&S::y>) and verifies both are resolvable with correct type names. ``` | |-ClassTemplateSpecializationDecl 0x234e2314800 <line:18:1, line:21:1> line:19:8 struct MemberData definition instantiated_from 0x234e2314110 implicit_instantiation | | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init | | | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr -- | | |-TemplateArgument decl '&S::x' | | | `-Field 0x234e2313ed0 'x' 'int' | | |-CXXRecordDecl 0x234e2314ac0 <col:1, col:8> col:8 implicit struct MemberData -- | `-ClassTemplateSpecializationDecl 0x234e25b5968 <line:18:1, line:21:1> line:19:8 struct MemberData definition instantiated_from 0x234e2314110 implicit_instantiation | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init | | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr -- | |-TemplateArgument decl '&S::y' | | `-Field 0x234e2313f38 'y' 'int' | |-CXXRecordDecl 0x234e25b5bd8 <col:1, col:8> col:8 implicit struct MemberData -- | |-ClassTemplateSpecializationDecl 0x234e25b7080 <line:27:1, line:30:1> line:28:8 struct MaybeNull definition instantiated_from 0x234e25b6b50 implicit_instantiation | | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init | | | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr -- | `-ClassTemplateSpecializationDecl 0x234e25b80c0 <line:27:1, line:30:1> line:28:8 struct MaybeNull definition instantiated_from 0x234e25b6b50 implicit_instantiation | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init | | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr -- | |-TemplateArgument decl '&g1' | | `-Var 0x234e25b69b8 'g1' 'int' | |-CXXRecordDecl 0x234e25b8338 <col:1, col:8> col:8 implicit struct MaybeNull ``` ``` lldb a.out -o "type lookup MemberData<&S::x>" -o "type lookup MemberData<&S::y>" -o quit (lldb) target create "a.out" Current executable set to 'a.out' (x86_64). (lldb) type lookup MemberData<&S::x> template<> struct MemberData<&S::x> { int get(S &); } (lldb) type lookup MemberData<&S::y> template<> struct MemberData<&S::y> { int get(S &); } ``` ## **Debugger Evidence** Collected at two `DW_TAG_template_value_parameter` DIEs during ParseTemplateDIE, both with name="PtrToMember" and `type_class=256` (eTypeClassMemberPointer): DIE `0xcb: uval64=8`; DIE `0x1e9 : uval64=24` These correspond to two members of Fiber: ``` DW_TAG_member "listHook_" DW_AT_data_member_location(0x08) ← uval64=8 DW_TAG_member "globalListHook_" DW_AT_data_member_location(0x18) ← uval64=24 ``` Clang's ground truth AST correctly produces TemplateArgument decl (kind=Declaration) with distinct FieldDecl references for each specialization. ``` 22:| | |-ClassTemplateSpecializationDecl 0x36d785b7cc0 <line:43:1, line:56:1> line:44:8 struct mhtraits definition instantiated_from 0x36d785b4238 implicit_instantiation 23-| | | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init 24-| | | | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr 25-| | | | |-CopyConstructor simple trivial has_const_param implicit_has_const_param 26-| | | | |-MoveConstructor exists simple trivial 27-| | | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param 28-| | | | |-MoveAssignment exists simple trivial needs_implicit 29-| | | | `-Destructor simple irrelevant trivial 30-| | | |-TemplateArgument type 'intrusive::Fiber' 31-| | | | `-RecordType 0x36d785b3fb0 'intrusive::Fiber' canonical 32-| | | | `-CXXRecord 0x36d78344850 'Fiber' 33-| | | |-TemplateArgument type 'intrusive::list_member_hook<>' 34-| | | | `-RecordType 0x36d785b3c20 'intrusive::list_member_hook<>' canonical 35-| | | | `-ClassTemplateSpecialization 0x36d78344b48 'list_member_hook' 36-| | | |-TemplateArgument decl '&intrusive::Fiber::listHook_' 37-| | | | `-Field 0x36d785b3e70 'listHook_' 'list_member_hook<>':'intrusive::list_member_hook<>' 38-| | | |-CXXRecordDecl 0x36d785b93a0 <col:1, col:8> col:8 implicit struct mhtraits 39-| | | |-TypeAliasDecl 0x36d785b9470 <line:45:3, col:22> col:9 referenced value_type 'intrusive::Fiber' 40-| | | | `-SubstTemplateTypeParmType 0x36d785b9430 'intrusive::Fiber' sugar typename depth 0 index 0 T 41-| | | | |-ClassTemplateSpecialization 0x36d785b7cc0 'mhtraits' 42-| | | | `-RecordType 0x36d785b3fb0 'intrusive::Fiber' canonical -- 78:| | `-ClassTemplateSpecializationDecl 0x36d785b8210 <line:43:1, line:56:1> line:44:8 struct mhtraits definition instantiated_from 0x36d785b4238 implicit_instantiation 79-| | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init 80-| | | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr 81-| | | |-CopyConstructor simple trivial has_const_param implicit_has_const_param 82-| | | |-MoveConstructor exists simple trivial 83-| | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param 84-| | | |-MoveAssignment exists simple trivial needs_implicit 85-| | | `-Destructor simple irrelevant trivial 86-| | |-TemplateArgument type 'intrusive::Fiber' 87-| | | `-RecordType 0x36d785b3fb0 'intrusive::Fiber' canonical 88-| | | `-CXXRecord 0x36d78344850 'Fiber' 89-| | |-TemplateArgument type 'intrusive::list_member_hook<>' 90-| | | `-RecordType 0x36d785b3c20 'intrusive::list_member_hook<>' canonical 91-| | | `-ClassTemplateSpecialization 0x36d78344b48 'list_member_hook' 92-| | |-TemplateArgument decl '&intrusive::Fiber::globalListHook_' 93-| | | `-Field 0x36d785b3f60 'globalListHook_' 'list_member_hook<>':'intrusive::list_member_hook<>' 94-| | |-CXXRecordDecl 0x36d785be080 <col:1, col:8> col:8 implicit struct mhtraits 95-| | |-TypeAliasDecl 0x36d785be150 <line:45:3, col:22> col:9 referenced value_type 'intrusive::Fiber' 96-| | | `-SubstTemplateTypeParmType 0x36d785be110 'intrusive::Fiber' sugar typename depth 0 index 0 T 97-| | | |-ClassTemplateSpecialization 0x36d785b8210 'mhtraits' 98-| | | `-RecordType 0x36d785b3fb0 'intrusive::Fiber' canonical ``` ### **Full Paste** ``` $ llvm-dwarfdump --debug-info=0x000000cb repro.dwp -c -p repro.dwp: file format elf64-x86-64 .debug_info.dwo contents: 0x00000014: DW_TAG_compile_unit DW_AT_producer ("clang version 22.1.20 ...) DW_AT_language (DW_LANG_C_plus_plus_14) DW_AT_name ("repro.cpp") DW_AT_dwo_name ("repro-repro.dwo") 0x0000001a: DW_TAG_namespace DW_AT_name ("intrusive") 0x000000b9: DW_TAG_structure_type DW_AT_calling_convention (DW_CC_pass_by_value) DW_AT_name ("mhtraits<intrusive::Fiber, intrusive::list_member_hook<(intrusive::link_mode_type)2>, &intrusive::Fiber::listHook_>") DW_AT_byte_size (0x01) DW_AT_decl_file (0x00) DW_AT_decl_line (44) 0x000000cb: DW_TAG_template_value_parameter DW_AT_type (0x0000022d "intrusive::list_member_hook<(intrusive::link_mode_type)2> intrusive::Fiber::*") DW_AT_name ("PtrToMember") DW_AT_const_value (8) ``` lldb prints: ``` p name (const char *) 0x000055f44554c4ca "PtrToMember" p uval64 (uint64_t) 8 p (unsigned)clang_type.GetTypeClass() (unsigned int) 256 ``` ``` $ llvm-dwarfdump --debug-info=0x000001e9 repro.dwp -c -p repro.dwp: file format elf64-x86-64 .debug_info.dwo contents: 0x00000014: DW_TAG_compile_unit DW_AT_producer ("clang version 22.1.20") DW_AT_language (DW_LANG_C_plus_plus_14) DW_AT_name ("repro.cpp") DW_AT_dwo_name ("repro-repro.dwo") 0x0000001a: DW_TAG_namespace DW_AT_name ("intrusive") 0x000001d7: DW_TAG_structure_type DW_AT_calling_convention (DW_CC_pass_by_value) DW_AT_name ("mhtraits<intrusive::Fiber, intrusive::list_member_hook<(intrusive::link_mode_type)2>, &intrusive::Fiber::globalListHook_>") DW_AT_byte_size (0x01) DW_AT_decl_file (0x00) DW_AT_decl_line (44) 0x000001e9: DW_TAG_template_value_parameter DW_AT_type (0x0000022d "intrusive::list_member_hook<(intrusive::link_mode_type)2> intrusive::Fiber::*") DW_AT_name ("PtrToMember") DW_AT_const_value (24) ``` lldb prints: ``` p name (const char *) 0x000055f44554c4ca "PtrToMember" p uval64 (uint64_t) 24 p (unsigned)clang_type.GetTypeClass() (unsigned int) 256 ```
Welcome to the LLVM project!
This repository contains the source code for LLVM, a toolkit for the construction of highly optimized compilers, optimizers, and run-time environments.
The LLVM project has multiple components. The core of the project is itself called “LLVM”. This contains all of the tools, libraries, and header files needed to process intermediate representations and convert them into object files. Tools include an assembler, disassembler, bitcode analyzer, and bitcode optimizer.
C-like languages use the Clang frontend. This component compiles C, C++, Objective-C, and Objective-C++ code into LLVM bitcode -- and from there into object files, using LLVM.
Other components include: the libc++ C++ standard library, the LLD linker, and more.
Consult the Getting Started with LLVM page for information on building and running LLVM.
For information on how to contribute to the LLVM project, please take a look at the Contributing to LLVM guide.
Join the LLVM Discourse forums, Discord chat, LLVM Office Hours or Regular sync-ups.
The LLVM project has adopted a code of conduct for participants to all modes of communication within the project.