Issue 250925.1: Support for C++ variadic variable captures

Author: Michael Buch
Champion: David Blaikie
Date submitted: 2025-09-25
Date revised: 2026-01-27
Date closed: 2026-02-02
Type: Enhancement
Status: Accepted
DWARF version: 6

This proposal is a follow-up to Issue 250516.1: Support C++0x Variadic templates to support variadic lambda captures and variadic structured binding declarations.

Motivation

DWARF v6 is scheduled to support two new tags to encode parameter packs (motivated by the C++ feature of the same name): DW_TAG_template_parameter_pack and DW_TAG_formal_parameter_pack. These tags allow producers to represent template parameter packs and function argument packs respectively.

With C++20, parameter packs can be used in lambda variable capture lists:

template<typename... Args>
auto f(Args&& args) {
  return [...args = std::forward<Args>(args)] {
     // Use args
  };
}

Here, args is actually a collection of init-capture declarations. In DWARF one might want to represent these as DW_TAG_members on the anonymous lambda structure. However, neither DW_TAG_formal_parameter_pack nor DW_TAG_template_parameter_pack would allow such a representation.

With C++26, parameter packs can be used in structured binding declarations:

struct C { int x = 1, y = 2, z = 3; };

template<int N>
void foo() {
  auto [d, ...e] = C();
}

Here, e is a collection of variable declarations. In DWARF one might want to represent these as DW_TAG_variables local to the function foo. But, again, neither of the upcoming parameter pack tags would allow this.

More discussion on this is available in LLVM issue #152282.

We considered adding a new tag DW_TAG_variable_pack, which functions similar to DW_TAG_template_parameter_pack and DW_TAG_formal_parameter_pack. The tag's purpose is to encode packs of variable declarations.

So we would end up with three pack tags covering five or six different packable tags — though the grouping for template_parameter_pack is somewhat useful: It makes it easier to scan a DW_TAG_subprogram looking for template parameters, or scan looking for formal parameters — you only have to look at the tags, not the attributes or the children, as you would if both formal parameter and template parameter tags used the same pack — something similar might apply to variable_pack, differentiating formal parameters from local variables, though not differentiating instance members (DW_TAG_member) from non-instance/static members (DW_TAG_variable).

On balance, it's better to create a DW_TAG_pack with a DW_AT_tag attribute that specifies the kind of things in the pack. This would have avoided the need for this proposal in the first place (as we'd have already had a more general purpose tool) and allowed for packs of things we've yet to discover/languages have yet to invent. (Although, understanding that while the spec wouldn't need to change for such packs, consumers wouldn't probably be implemented with such generality that they don't need updating when DW_TAG_pack shows up in new places in the future — that's OK.)

Proposal

Revert changes made in 250516.1 Support C++0x Variadic templates (with the exception of the examples in Appendix D; see below).

Add the following new tag to Table 2.1, "Tag names", to Table 7.3, "Tag encodings", and to Table A.1 "Attributes by tag value":

Add the following new attribute to Table 2.2:

Attribute Identifies or Specifies
DW_AT_tag the DW_TAG type of potential children of a pack.

Introduce a new subsection in Chapter 2:

2.XX Packs

In C++, packs are used to introduce zero or more entities of the same kind (template type, value, or template parameter, lambda init-capture, structured binding, etc) with a single name.

A pack of entities with a single name is represented by a debugging information entry with the tag DW_TAG_pack. A pack must have a DW_AT_tag attribute that specifies the tag of the children of the pack (even if the pack has no children).

A pack may have zero or more children of the kind specified by the DW_AT_tag attribute of the pack.

[How much should we say about where these can appear? We don't say much about where DW_TAG_label can appear, for instance — so I figure it's fine to do similarly here & let folks use it wherever they find it useful?]

Retain Section D.20, "Variadic Template Examples", from the original proposal, but replace the contents of Figure D.92, "Recursive Part of Variadic Template Example DWARF Encoding", with the following:

! 1) printf<int,char,int> (const char* s, int value, char, int);
!        // args => char, int
10$:  DW_TAG_subprogram
          DW_AT_name("printf")
11$:      DW_TAG_template_type_parameter
              DW_AT_name("T")
              DW_AT_type(reference to base type "int")
12$:      DW_TAG_pack
              DW_AT_name("PackTypes")
              DW_AT_tag(DW_TAG_template_type_parameter)
13$:          DW_TAG_template_type_parameter
                  ! no DW_AT_name attribute
                  DW_AT_type(reference to base type "char")
14$:          DW_TAG_template_type_parameter
                  ! no DW_AT_name attribute
                  DW_AT_type(reference to base type "int")
15$:      DW_TAG_formal_parameter
              DW_AT_name("s")
              DW_AT_type(reference to type "const char*")
16$:      DW_TAG_formal_parameter
              DW_AT_name("value")
              DW_AT_type(reference to base type "int")
17$:      DW_TAG_pack
              DW_AT_name("args")
              DW_AT_tag(DW_TAG_formal_paramter)
18$:          DW_TAG_formal_parameter
                  ! no DW_AT_name attribute
                  DW_AT_type(reference to base type "char")
19$:          DW_TAG_formal_parameter
                  ! no DW_AT_name attribute
                  DW_AT_type(reference to base type "int")

! 2) printf<char,int>     (const char* s, char value, int);
!        // args => int
20$:  DW_TAG_subprogram
          DW_AT_name("printf")
21$:      DW_TAG_template_type_parameter
              DW_AT_name("T")
              DW_AT_type(reference to base type "int")
22$:      DW_TAG_pack
              DW_AT_name("PackTypes")
              DW_AT_tag(DW_TAG_template_type_paramter)
23$:          DW_TAG_template_type_parameter
                  ! no DW_AT_name attribute
                  DW_AT_type(reference to base type "int")
24$:      DW_TAG_formal_parameter
              DW_AT_name("s")
              DW_AT_type(reference to type "const char*")
25$:      DW_TAG_formal_parameter
              DW_AT_name("value")
              DW_AT_type(reference to base type "char")
26$:      DW_TAG_pack
              DW_AT_name("args")
              DW_AT_tag(DW_TAG_formal_paramter)
27$:          DW_TAG_formal_parameter
                  ! no DW_AT_name attribute
                  DW_AT_type(reference to base type "int")

! 3) printf<int>          (const char* s, int value);
!        // args => (empty)
30$:  DW_TAG_subprogram
          DW_AT_name("printf")
31$:      DW_TAG_template_type_parameter
              DW_AT_name("T")
              DW_AT_type(reference to base type "int")
32$:      DW_TAG_pack
              DW_AT_name("PackTypes")
              DW_AT_tag(DW_TAG_template_type_parameter)
33$:      DW_TAG_formal_parameter
              DW_AT_name("s")
              DW_AT_type(reference to type "const char*")
34$:      DW_TAG_formal_parameter
              DW_AT_name("value")
              DW_AT_type(reference to base type "int")
35$:      DW_TAG_pack
              DW_AT_name("args")
              DW_AT_tag(DW_TAG_formal_parameter)

2026-01-27: Revised proposal to revert earlier proposal and introduce more general DW_TAG_pack.

2026-02-02: Accepted.