https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126267
Bug ID: 126267
Summary: [debug, c++] Empty DW_TAG_GNU_template_parameter_pack:
template arguments bound to a parameter pack are
omitted from debug info
Product: gcc
Version: 16.1.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: debug
Assignee: unassigned at gcc dot gnu.org
Reporter: Konstantin.Bolshakov2 at infineon dot com
Target Milestone: ---
=======================================================================
PRODUCT / COMPONENT
=======================================================================
- Product: gcc
- Component: debug (DWARF debug information)
- Keywords: wrong-debug
- Known to fail: 15.2.0, 16.1.0
- Host/target: x86_64-pc-linux-gnu
=======================================================================
SUMMARY
=======================================================================
When g++ instantiates a variadic template, the debug info for the
instantiation contains a DW_TAG_GNU_template_parameter_pack DIE that is
empty: it has no DW_TAG_template_type_parameter children and no
DW_AT_name. As a result, the concrete template arguments that the pack
expands to are completely absent from the DWARF. A consumer reading the
debug info cannot reconstruct the template argument list of the
specialization.
The DW_TAG_GNU_template_parameter_pack abbreviation is emitted with
DW_CHILDREN_no, so the DIE structurally cannot carry the arguments at all.
The defect is triggered deterministically when the template parameter pack
is unnamed (e.g. "template <typename...> class a"). When the same pack is
given a name ("template <typename... Args> class a"), GCC emits both the
DW_AT_name and the DW_TAG_template_type_parameter children correctly.
=======================================================================
REPRODUCER
=======================================================================
example.cpp:
template <typename...> class a {};
template <typename T> class e { a<T> _M_t; };
int main() {
e<char> f;
e<int> g;
return 0;
}
Compile:
g++ -std=c++20 -g -O0 -c example.cpp -o example.o
(The bug reproduces with -std=c++11 through -std=c++23, with both -gdwarf-4
and the default -gdwarf-5, and at -O0.)
Compiler Explorer (side-by-side llvm-dwarfdump of GCC 15.2 vs clang 22.1.0
for the same source): https://godbolt.org/z/5j4ETYq5a
=======================================================================
ACTUAL DWARF (GCC 15.2.0)
=======================================================================
Dump of the two a<...> instantiations (produced with llvm-dwarfdump, but the
raw abbreviation table shows the same thing):
0x00000033: DW_TAG_class_type
DW_AT_name ("a<char>")
DW_AT_byte_size (1)
DW_AT_decl_line (1)
0x0000003e: DW_TAG_GNU_template_parameter_pack <-- empty
0x0000003f: NULL <-- pack has no
children
0x0000005c: DW_TAG_class_type
DW_AT_name ("a<int>")
DW_AT_byte_size (1)
0x00000067: DW_TAG_GNU_template_parameter_pack <-- empty
0x00000068: NULL
The abbreviation confirms the pack is declared with no children:
[2] DW_TAG_GNU_template_parameter_pack DW_CHILDREN_no
a<char> and a<int> are full definitions (they carry DW_AT_byte_size and have
no DW_AT_declaration), yet nothing in their debug info records that the first
was instantiated with char and the second with int. The two definitions are
indistinguishable in DWARF apart from their DW_AT_name string.
=======================================================================
EXPECTED DWARF
=======================================================================
The pack DIE should carry one DW_TAG_template_type_parameter child per
argument the pack expands to, as GCC already does for named packs and as
clang does unconditionally.
Clang 22.1.0 on the identical source:
0x00000067: DW_TAG_class_type
DW_AT_name ("a<char>")
0x0000006d: DW_TAG_GNU_template_parameter_pack
0x0000006e: DW_TAG_template_type_parameter
DW_AT_type (0x00000063 "char") <-- argument
preserved
0x00000073: NULL
0x00000074: NULL
=======================================================================
ROOT CAUSE CORRELATION (unnamed vs named pack)
=======================================================================
Naming the pack makes GCC emit the arguments. Both class and function
templates are affected identically.
template <typename... Args> class a {}; // NAMED pack
template <typename...> class b {}; // UNNAMED pack
int main() { a<int> x; b<int> y; return 0; }
GCC 15.2.0 output:
a<int>:
0x0000003d: DW_TAG_GNU_template_parameter_pack
DW_AT_name ("Args")
0x00000042: DW_TAG_template_type_parameter
DW_AT_type (0x0000008e "int") <-- argument present
0x00000047: NULL
0x00000048: NULL
b<int>:
0x00000053: DW_TAG_GNU_template_parameter_pack <-- empty
0x00000054: NULL <-- argument dropped
The same holds for function templates: a named pack produces populated
DW_TAG_template_type_parameter children; an unnamed pack produces an empty
DW_TAG_GNU_template_parameter_pack followed immediately by NULL.
=======================================================================
REAL-WORLD IMPACT (libstdc++ std::tuple)
=======================================================================
This is not limited to contrived unnamed packs. In a single translation unit
that uses std::tuple<int, char, double>, GCC emits the outer std::tuple<...>
definition with an empty, unnamed DW_TAG_GNU_template_parameter_pack, while
its std::_Tuple_impl<...> base classes get correctly populated packs named
_Elements:
populated pack (name "_Elements") in type: std::_Tuple_impl<2, double>
populated pack (name "_Elements") in type: std::_Tuple_impl<1, char,
double>
populated pack (name "_Elements") in type: std::_Tuple_impl<0, int, char,
double>
EMPTY pack (no name) in type: std::tuple<int, char, double>
Because std::tuple is the storage type behind std::unique_ptr,
std::shared_ptr, and many other standard components, the concrete element
types of these ubiquitous templates are missing from the debug information a
consumer would use to describe them.
=======================================================================
NOTES
=======================================================================
- DW_TAG_GNU_template_parameter_pack is a GNU extension, so an empty pack is
not a hard DWARF standard violation, but it is lossy: the specialization's
template argument list is unrecoverable from the debug info. GCC clearly
intends to record these arguments (it does so for named packs and for
_Tuple_impl above), so the empty form looks like an omission bug rather
than a deliberate design choice.
- The linkage names of the affected instantiations still reference the
template parameters (e.g. _ZNSt5tupleIJicdEE...), so the argument
information exists in the compiler at emission time; only the debug-info
DIE children are dropped.
=======================================================================
ENVIRONMENT
=======================================================================
Compiler : g++ (GCC) 15.2.0 (also reproduces on 16.1.0)
Producer string : GNU C++20 15.2.0 -mtune=generic -march=x86-64 -g -O0
-std=c++20
DWARF version : 5 (default); also reproduces with -gdwarf-4
Standards : reproduces with -std=c++11 .. -std=c++23
Target : x86_64-pc-linux-gnu
Dump tool : llvm-dwarfdump (the abbreviation table shows
DW_CHILDREN_no)