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)

Reply via email to