================
@@ -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

Reply via email to