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.