================
@@ -3343,6 +3343,55 @@ class TemplateParamObjectDecl : public ValueDecl,
static bool classofKind(Kind K) { return K == TemplateParamObject; }
};
+/// Represents a C++26 expansion statement declaration.
+///
+/// This is a bit of a hack, since expansion statements shouldn't really be
+/// "declarations" per se (they don't declare anything). Nevertheless, we *do*
+/// need them to be declaration *contexts*, because the DeclContext is used to
+/// compute the "template depth" of entities enclosed therein. In particular,
+/// the "template depth" is used to find instantiations of parameter variables,
+/// and a lambda enclosed within an expansion statement cannot compute its
+/// template depth without a pointer to the enclosing expansion statement.
+class ExpansionStmtDecl : public Decl, public DeclContext {
+ CXXExpansionStmt *Expansion = nullptr;
+ TemplateParameterList *TParams;
+ CXXExpansionInstantiationStmt *Instantiations = nullptr;
----------------
Sirraide wrote:
> Hmm... I see, so this is actually just a SINGLE instantiation when done in a
> non-dependent context? The name threw me a bit. We wouldn't expect the
> 'dependent' components to this to actually end up anywhere else/be
> changeable, right?
> So the process is effectively to finish this context, then 'step back' into
> it and do a tree-transform/instantiation?
I’m not sure I explained that too well earlier, and the terminology here is a
bit confusing (at least I keep getting confused by it when trying to explain
it, because ‘instantiating’ means 2 different thigns here).
To rephrase this a bit, let’s distinguish between ‘expanding’ an expansion
statement (i.e. instantiating its body N times) from ‘instantiating’ the entire
thing (because it just happens to be in a template, and we instantiate the
containing template); what I meant was, after it’s been ‘expanded’ (which
happens as soon as we parse it if the expansion *size* `N` is not dependent),
if we *then* instantiate the containing template, we only ‘instantiate’ the
expanded statements (and not e.g. the original expansion-initializer).
In the AST, the `ExpansionStmtDecl` is the top-level node for an expansion
statement (it itself is inside a `DeclStmt` because it has to be but that’s
irrelevant here). An expansion statement essentially has 2 representations
(this is similar to the difference between syntactic/semantic form of
initializer lists—I *think*; it’s been a while since I looked at the init list
code):
1. the `CXXExpansionStmt` (specifically, one of its derived classes) which
contains all the parts needed to perform the expansion, and
2. the `CXXExpansionInstantiationStmt`, which contains the expanded/desugared
AST produced by the expansion.
The latter is only present after expansion (and it is what’s used for constant
evaluation and codegen); additionally, once we’ve performed expansion, we never
touch the `CXXExpansionStmt` again, even if parts of it are still dependent
(which can happen if the expansion statement is inside a template).
For example, if the user writes
```c++
template <typename T>
void f() {
template for (auto x : {T(1), T(2)}) {
T a = x;
}
}
```
the expansion statement is ‘expanded’ immediately (because even though `{T(1),
T(2)}` is instantiation-dependent, the size is known to be `2`), i.e. the
template effectively becomes
```c++
template <typename T>
void f() {
{ // Note: This outer ‘compound statement’ is actually an ExpansionStmtDecl
whose
// 'ExpansionInstantiationStmt' contains the following 2 compound
statements:
{ T a = T(1); }
{ T a = T(2); }
}
}
```
(of course, we still preserve e.g. the `{T(1), T(2)}` expression in the AST).
If we then instantiate e.g. `f<int>`, the instantiation effectively becomes
```c++
void f<int>() {
{ // Note: This outer ‘compound statement’ is the instantiated
ExpansionStmtDecl.
{ int a = int(1); }
{ int a = int(2); }
}
}
```
Notably, the original expansion-initializer, i.e. `{T(1), T(2)}`, is *not*
instantiated (since it isn’t needed anymore); that is, we *don’t* end up w/
e.g. `{int(1), int(2)}` in the AST for `f<int>`, but we instead preserve the
original `{T(1), T(2)}`. That is, the `ExpansionStmtDecl` in `f<int>` has both
the *original* `CXXExpansionStmt` as written in the template `f`, as well as an
‘instantiation’ of the `ExpansionInstantiationStmt` that was ‘expanded’ when we
parsed the template.
https://github.com/llvm/llvm-project/pull/165195
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits