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

Comparing original with latest. [ Return to the latest version ]

## Synopsis

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

## Motivation 

DWARFv6 is scheduled to support two new tags to encode
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_member`s 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_variable`s
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][3].
More discussion on this is available in [LLVM issue #152282][llvm152282].

We propose a new tag `DW_TAG_variable_pack`, which functions
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.

## Document changes
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][250516.1]
(with the exception of the examples in Appendix D; see below).

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

* `DW_TAG_variable_pack`
* `DW_TAG_pack`

  * Allowable attributes:
    Allowable attributes:

    * DECL
    * `DECL`
    * `DW_AT_name`
    * `DW_AT_sibling`
    * `DW_AT_tag`

Add a new sub-chapter in Chapter 4 Data Object and Object List Entries:
Add the following new attribute to Table 2.2:

> Chapter 4.4 Variable Packs
> | 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 is a named entity that represents arbitrary many
> variable declarations. Indexing into the entity to
> retrieve individual members of the pack is language
> dependent. Such program entities may be represented in
> DWARF using `DW_TAG_variable_pack`. The `DW_AT_name`
> attribute can be used specify the name of the pack entity
> in the source.
>
> For each variable declaration introduced by the pack in
> the source, the `DW_TAG_variable_pack` has a
> `DW_TAG_variable` or `DW_TAG_member` child. The order of
> the children is the same as the order in which the
> variable declarations are introduced in the source.
> 
> 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).
> <non-normative>
> *DW_TAG_template_parameter_pack and
> DW_TAG_function_parameter_pack describe template and
> function parameter packs respectively. In C++, packs can
> also represent collections of variable declarations. For
> example, a lambda capture list may capture a pack of
> variables or a structured binding may decompose to a pack
> of variables (see examples in Appendix D.17).*
> </non-normative>

Add a new example to Appendix D: Examples:

> Appendix D.17 Variable Pack Examples
> 
> 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
> Consider following C++20 lambda:
>
>     1 template<typename... Args>
>     2 auto f(Args&& args) {
>     3   return [...captured = std::forward<Args>(args)] {
>     4     return captured...[0] + captured...[1]; // Pack indexing since C++26
>     5   };
>     6 }
>     7
>     8 int main() { auto x = f(1, 2); }
> 
> Here, `captured` is a collection of init-capture
> declarations. A DWARF producer may choose to represent the
> pack members as `DW_TAG_member`s on the anonymous lambda
> structure:
> 
>     DW_TAG_subprogram
    10$:  DW_TAG_subprogram
>         DW_AT_name("f")
>         ! Anonymous lambda structure
>         DW_TAG_class_type
>           DW_TAG_variable_pack
>             DW_AT_decl_line(3)
>             DW_AT_name("captured")
              DW_AT_name("printf")
>             DW_TAG_member
>               ! Optional name a producer may want to give the pack children
    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("captured:0")
                  DW_AT_name("value")
                  DW_AT_type(reference to base type "int")
    17$:      DW_TAG_pack
>               DW_AT_data_member_location(0x0)
>             DW_TAG_member
>               ! Optional name a producer may want to give the pack children
>               DW_AT_name("captured:1")
                  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
>               DW_AT_data_member_location(0x4)
> 
> Consider following C++26 structured binding pack declaration:
>
>     1 struct C { int x = 1, y = 2, z = 3; };
>     2
>     3 template<int N>
>     4 void foo() {
>     5   auto [d, ...e] = C();
>     6 }
> 
> Here, `e` is a collection of variable declarations. A
> DWARF producer may represent the pack members as
> `DW_TAG_variable`s:
>
>     DW_TAG_subprogram
    20$:  DW_TAG_subprogram
>         DW_AT_name("foo")
>         DW_TAG_variable_pack
>             DW_AT_decl_line(5)
>             DW_AT_name("e")
              DW_AT_name("printf")
    21$:      DW_TAG_template_type_parameter
>             DW_TAG_variable
>               ! Optional name a producer may want to give the pack children
>               DW_AT_name("e:0")
                  DW_AT_name("T")
>               DW_AT_location(...)
>             DW_TAG_member
>               ! Optional name a producer may want to give the pack children
                  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("e:1")
                  DW_AT_name("s")
>               DW_AT_location(...)
                  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)


[1]: https://dwarfstd.org/issues/250516.1.html
[250516.1]: https://dwarfstd.org/issues/250516.1.html
[2]: https://en.cppreference.com/w/cpp/language/parameter_pack.html
[parameterpack]: https://cppreference.net/cpp/language/parameter_pack.html
[3]: https://github.com/llvm/llvm-project/issues/152282
[llvm152282]: https://github.com/llvm/llvm-project/issues/152282

---

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

2026-02-02: Accepted.