Skip to content
Open
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
f8bd65d
Enumerating expansion statements mostly work
Sirraide Oct 24, 2025
b864b4c
Fix some instantiation issues and add codegen tests
Sirraide Oct 24, 2025
cb66211
Support pack expansion in enumerating expansion statements
Sirraide Oct 24, 2025
34960d6
Iterating expansion statements
Sirraide Oct 25, 2025
dbf5fec
Dependent expansion statements
Sirraide Oct 25, 2025
a9e1d55
Sema for destructuring expansion statements
Sirraide Oct 25, 2025
86cab85
Implement CWG 3044
Sirraide Oct 26, 2025
1deea2b
Add codegen tests for destructuring expansion statements
Sirraide Oct 26, 2025
a365f3e
Add tests for break/continue
Sirraide Oct 26, 2025
355b761
Disallow (non-local) labels in expansion statements
Sirraide Oct 26, 2025
3603a54
Disallow case/default statements in expansion statements
Sirraide Oct 26, 2025
316f81a
Add another case/default test
Sirraide Oct 26, 2025
9e43b8c
Add comment explaining why we diagnose this here
Sirraide Oct 26, 2025
66ca06c
Proper support for local labels
Sirraide Oct 26, 2025
278977a
Make substatement memory representation less horrible
Sirraide Oct 26, 2025
7ba15b0
Update out-of-date comment
Sirraide Oct 26, 2025
fb7a000
Check for unexpanded parameter packs
Sirraide Oct 26, 2025
9264ff0
Add -fexpansion-limit
Sirraide Oct 26, 2025
b0468c7
Add release note
Sirraide Oct 26, 2025
a2b72d1
Add StmtPrinter/TextNodeDumper/Serialisation tests
Sirraide Oct 26, 2025
a217f90
Implement CWG 3061
Sirraide Oct 26, 2025
a9f4244
Add a test for CWG 3048
Sirraide Oct 26, 2025
bfdb5ed
Add a test for lifetime extension (CWG 3034)
Sirraide Oct 26, 2025
6739929
Fix lifetime extension in templates
Sirraide Oct 27, 2025
ea2a347
Remove commented-out code
Sirraide Oct 27, 2025
d10d82a
ASTImporter
Sirraide Oct 27, 2025
1ae64d3
Minor fixes
Sirraide Oct 27, 2025
9b09c05
clang-format
Sirraide Oct 27, 2025
8e6a4af
clang-format, again
Sirraide Oct 27, 2025
84a3d71
Update LanguageExtensions.rst
Sirraide Oct 27, 2025
ddede95
Add extension warning test
Sirraide Oct 27, 2025
b188e7c
Apply code review suggestions
Sirraide Oct 27, 2025
5a0c30f
Handle returning from an expansion properly in constant evaluation
Sirraide Oct 27, 2025
61d7899
Merge branch 'main' into expansion-statements
Sirraide Oct 29, 2025
17d05ae
Actually make this test expand to nothing
Sirraide Oct 29, 2025
4965e7d
Amend diagnostic wording
Sirraide Nov 3, 2025
79af6ec
Only move up to the parent context during expansion
Sirraide Nov 3, 2025
3642092
Add tests for destructuring using tuple_size
Sirraide Nov 3, 2025
2ff4de2
Perform overload resolution instead of just ADL
Sirraide Nov 12, 2025
4c2b4aa
Add a bunch of documentation
Sirraide Nov 12, 2025
3de01e9
Add 'Example:'
Sirraide Nov 12, 2025
8437f22
Use a C array instead of std::array
Sirraide Nov 12, 2025
7915983
Use 'std::tuple' in examples instead of a C array
Sirraide Nov 12, 2025
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1784,6 +1784,7 @@ Pack Indexing __cpp_pack_indexing C
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
Variadic Friends __cpp_variadic_friend C++26 C++03
Trivial Relocatability __cpp_trivial_relocatability C++26 C++03
Expansion Statements C++26 C++03
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we wait before backporting?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure; I can make it C++26 only for now

--------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
``_Complex`` (N693) C99 C89, C++
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ C++2c Feature Support
At this timem, references to constexpr and decomposition of *tuple-like* types are not supported
(only arrays and aggregates are).

- Implemented `P1306R5 <https://wg21.link/P1306R5>`_ Expansion Statements.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're not changing the Feature Test Macro here, I'd suggest we elaborate here that this is a partial implementation, perhaps mentioning our concerns.


C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/AST/ASTNodeTraverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,12 @@ class ASTNodeTraverser
}
}

void VisitExpansionStmtDecl(const ExpansionStmtDecl *Node) {
Visit(Node->getExpansionPattern());
if (Traversal != TK_IgnoreUnlessSpelledInSource)
Visit(Node->getInstantiations());
}

void VisitCallExpr(const CallExpr *Node) {
for (const auto *Child :
make_filter_range(Node->children(), [this](const Stmt *Child) {
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/ComputeDependence.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class DesignatedInitExpr;
class ParenListExpr;
class PseudoObjectExpr;
class AtomicExpr;
class CXXExpansionInitListExpr;
class ArraySectionExpr;
class OMPArrayShapingExpr;
class OMPIteratorExpr;
Expand Down Expand Up @@ -191,6 +192,8 @@ ExprDependence computeDependence(ParenListExpr *E);
ExprDependence computeDependence(PseudoObjectExpr *E);
ExprDependence computeDependence(AtomicExpr *E);

ExprDependence computeDependence(CXXExpansionInitListExpr *E);

ExprDependence computeDependence(ArraySectionExpr *E);
ExprDependence computeDependence(OMPArrayShapingExpr *E);
ExprDependence computeDependence(OMPIteratorExpr *E);
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
if (getKind() != Decl::Var && getKind() != Decl::Decomposition)
return false;
if (const DeclContext *DC = getLexicalDeclContext())
return DC->getRedeclContext()->isFunctionOrMethod();
return DC->getEnclosingNonExpansionStatementContext()
->getRedeclContext()
->isFunctionOrMethod();
return false;
}

Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -2195,6 +2195,8 @@ class DeclContext {
return getDeclKind() == Decl::RequiresExprBody;
}

bool isExpansionStmt() const { return getDeclKind() == Decl::ExpansionStmt; }

bool isNamespace() const { return getDeclKind() == Decl::Namespace; }

bool isStdNamespace() const;
Expand Down Expand Up @@ -2292,6 +2294,15 @@ class DeclContext {
return const_cast<DeclContext *>(this)->getOuterLexicalRecordContext();
}

/// Retrieve the innermost enclosing context that doesn't belong to an
/// expansion statement. Returns 'this' if this context is not an expansion
/// statement.
DeclContext *getEnclosingNonExpansionStatementContext();
const DeclContext *getEnclosingNonExpansionStatementContext() const {
return const_cast<DeclContext *>(this)
->getEnclosingNonExpansionStatementContext();
}

/// Test if this context is part of the enclosing namespace set of
/// the context NS, as defined in C++0x [namespace.def]p9. If either context
/// isn't a namespace, this is equivalent to Equals().
Expand Down
49 changes: 49 additions & 0 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a little odd? Or am I missing something? Why would a decl need its list of instantiations?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the way I’ve set this up is, if the expansion statement has been expanded, we never instantiate it again and instead only instantiate the expansions (because we’d just be spending time instantiating something that will never be used for anything).

We could probably also most this into CXXExpansionStmt; iirc I tried that, but it was ultimately easier to just put it here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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

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

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooof, all of that is pretty subtle, and I fear/suspect I'm going to spend a lot of time re-reading the above as I try to review anything derived from this.

I wonder if we could start on JUST the ExpansionStmtDecl upstreaming. It wouldn't do much besides parsing, but at that point we could add expansions 1 by 1 as we figure out which ones we can need/use.

I might also suggest trying to un-overload the 'instantiation' phrase here, nad see if we could call it 'expanded' or something like that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could start on JUST the ExpansionStmtDecl upstreaming. It wouldn't do much besides parsing, but at that point we could add expansions 1 by 1 as we figure out which ones we can need/use.

The main complication w/ that is that because it adds a new DeclContext, there are a bunch of DeclContext-related changes in like a dozen files that I had to make, and my worry is that we might introduce either unneeded changes or forget about some of them if we upstream just ExpansionStmtDecl w/o a way to test how it’s used. I can try extracting all of that and see what we’d end up w/ if you think it’s worth it though.

patches that are MOSTLY mechanical for stuff like that is way easier to review.

I might also suggest trying to un-overload the 'instantiation' phrase here, nad see if we could call it 'expanded' or something like that.

Yeah, it’s definitely annoying that the standard refers to this process of ‘expanding’ as ‘instantiating’. CXXExpandedExpansionStmt sounds a bit silly but something like would be an option; let me know if you can think of a better name...

yeah, that is unfortunate but understandable based on their concept. Perhaps leave it instantiate/etc but be REALLY careful with comments here.

Ideally, we’d not make this a separate AST node and instead just store all of the expansions in either ExpansionStmtDecl or CXXExpansionStmt, but both of those are created before we get to the expansion, so at that point we have no idea how many expansions we’ll have, so we can’t exactly allocate trailing memory for it... (just reusing e.g. CompoundStmt for this doesn’t work because we need special handling for break/continue when we codegen/evaluate it).

Yeah, that is unfortunate. Its too bad we couldn't have talked about this before it got standardized and suggested nixing those two:)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps leave it instantiate/etc but be REALLY careful with comments here.

I should probably add (an abridged version of) my big comment above as a comment

Yeah, that is unfortunate. Its too bad we couldn't have talked about this before it got standardized and suggested nixing those two:)

I mean, of all the things that are complicated about this, I feel like break/continue aren’t that bad...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I’ve added some more documentation and examples to the various AST nodes; let me know if this makes the purpose of individual nodes clearer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you research the possibility of making non-decl decl contexts?
This is not really the first time we need something like that and it would be nice to be able to say

CXXExpansionStmt : public DeclContext, public Stmt

(alternatively, decoupling template depth tracking from decl context, this is very similar to our lambdas issues @mizvekov )

FYI, I fully realize I'm asking for a pony here, I just want to know if any exploration was done.

In the meantime, maybe we could find a better naming scheme
like

 - CXXExpansionStmtDeclContext - CXXExpansionStmtPattern - CXXExpansionStmtInstantiation 
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you research the possibility of making non-decl decl contexts? This is not really the first time we need something like that and it would be nice to be able to say

CXXExpansionStmt : public DeclContext, public Stmt

I briefly looked into that, but there are enough places where we do cast<Decl>(some_decl_context) or rely on some_decl_context->getKind() to return a DeclKind to the point that I gave up on that; I have a feeling that that’d be a major refactor.

(alternatively, decoupling template depth tracking from decl context)

I’m candidly not familiar enough with our template engine to know what exactly that would entail.

In the meantime, maybe we could find a better naming scheme like

 - CXXExpansionStmtDeclContext - CXXExpansionStmtPattern - CXXExpansionStmtInstantiation 

I’m pretty bad at naming so that’s definitely better than what I came up with. I’ll change it to that instead.


ExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
TemplateParameterList *TParams);

public:
friend class ASTDeclReader;

static ExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation Loc,
TemplateParameterList *TParams);
static ExpansionStmtDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);

CXXExpansionStmt *getExpansionPattern() { return Expansion; }
const CXXExpansionStmt *getExpansionPattern() const { return Expansion; }
void setExpansionPattern(CXXExpansionStmt *S) { Expansion = S; }

CXXExpansionInstantiationStmt *getInstantiations() { return Instantiations; }
const CXXExpansionInstantiationStmt *getInstantiations() const {
return Instantiations;
}

void setInstantiations(CXXExpansionInstantiationStmt *S) {
Instantiations = S;
}

NonTypeTemplateParmDecl *getIndexTemplateParm() const {
return cast<NonTypeTemplateParmDecl>(TParams->getParam(0));
}
TemplateParameterList *getTemplateParameters() const { return TParams; }

SourceRange getSourceRange() const override LLVM_READONLY;

static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == ExpansionStmt; }
};

inline NamedDecl *getAsNamedDecl(TemplateParameter P) {
if (auto *PD = P.dyn_cast<TemplateTypeParmDecl *>())
return PD;
Expand Down
150 changes: 150 additions & 0 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -5501,6 +5501,156 @@ class BuiltinBitCastExpr final
}
};

/// Represents an expansion-init-list to be expanded over by an expansion
/// statement.
class CXXExpansionInitListExpr final
: public Expr,
llvm::TrailingObjects<CXXExpansionInitListExpr, Expr *> {
friend class ASTStmtReader;
friend TrailingObjects;

const unsigned NumExprs;
SourceLocation LBraceLoc;
SourceLocation RBraceLoc;

CXXExpansionInitListExpr(EmptyShell ES, unsigned NumExprs);
CXXExpansionInitListExpr(ArrayRef<Expr *> Exprs, SourceLocation LBraceLoc,
SourceLocation RBraceLoc);

public:
static CXXExpansionInitListExpr *Create(const ASTContext &C,
ArrayRef<Expr *> Exprs,
SourceLocation LBraceLoc,
SourceLocation RBraceLoc);

static CXXExpansionInitListExpr *
CreateEmpty(const ASTContext &C, EmptyShell Empty, unsigned NumExprs);

ArrayRef<Expr *> getExprs() const { return getTrailingObjects(NumExprs); }
MutableArrayRef<Expr *> getExprs() { return getTrailingObjects(NumExprs); }
unsigned getNumExprs() const { return NumExprs; }

bool containsPackExpansion() const;

SourceLocation getBeginLoc() const { return getLBraceLoc(); }
SourceLocation getEndLoc() const { return getRBraceLoc(); }

SourceLocation getLBraceLoc() const { return LBraceLoc; }
SourceLocation getRBraceLoc() const { return RBraceLoc; }

child_range children() {
const_child_range CCR =
const_cast<const CXXExpansionInitListExpr *>(this)->children();
return child_range(cast_away_const(CCR.begin()),
cast_away_const(CCR.end()));
}

const_child_range children() const {
Stmt **Stmts = getTrailingStmts();
return const_child_range(Stmts, Stmts + NumExprs);
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXExpansionInitListExprClass;
}

private:
Stmt **getTrailingStmts() const {
return reinterpret_cast<Stmt **>(const_cast<Expr **>(getTrailingObjects()));
}
};

/// Helper that selects an expression from an expansion init list depending
/// on the current expansion index.
class CXXExpansionInitListSelectExpr : public Expr {
friend class ASTStmtReader;

enum SubExpr { RANGE, INDEX, COUNT };
Expr *SubExprs[COUNT];

public:
CXXExpansionInitListSelectExpr(EmptyShell Empty);
CXXExpansionInitListSelectExpr(const ASTContext &C,
CXXExpansionInitListExpr *Range, Expr *Idx);

CXXExpansionInitListExpr *getRangeExpr() {
return cast<CXXExpansionInitListExpr>(SubExprs[RANGE]);
}

const CXXExpansionInitListExpr *getRangeExpr() const {
return cast<CXXExpansionInitListExpr>(SubExprs[RANGE]);
}

void setRangeExpr(CXXExpansionInitListExpr *E) { SubExprs[RANGE] = E; }

Expr *getIndexExpr() { return SubExprs[INDEX]; }
const Expr *getIndexExpr() const { return SubExprs[INDEX]; }
void setIndexExpr(Expr *E) { SubExprs[INDEX] = E; }

SourceLocation getBeginLoc() const { return getRangeExpr()->getBeginLoc(); }
SourceLocation getEndLoc() const { return getRangeExpr()->getEndLoc(); }

child_range children() {
return child_range(reinterpret_cast<Stmt **>(SubExprs),
reinterpret_cast<Stmt **>(SubExprs + COUNT));
}

const_child_range children() const {
return const_child_range(
reinterpret_cast<Stmt **>(const_cast<Expr **>(SubExprs)),
reinterpret_cast<Stmt **>(const_cast<Expr **>(SubExprs + COUNT)));
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXExpansionInitListSelectExprClass;
}
};

class CXXDestructuringExpansionSelectExpr : public Expr {
friend class ASTStmtReader;

DecompositionDecl *Decomposition;
Expr *Index;

public:
CXXDestructuringExpansionSelectExpr(EmptyShell Empty);
CXXDestructuringExpansionSelectExpr(const ASTContext &C,
DecompositionDecl *Decomposition,
Expr *Index);

DecompositionDecl *getDecompositionDecl() {
return cast<DecompositionDecl>(Decomposition);
}

const DecompositionDecl *getDecompositionDecl() const {
return cast<DecompositionDecl>(Decomposition);
}

void setDecompositionDecl(DecompositionDecl *E) { Decomposition = E; }

Expr *getIndexExpr() { return Index; }
const Expr *getIndexExpr() const { return Index; }
void setIndexExpr(Expr *E) { Index = E; }

SourceLocation getBeginLoc() const { return Decomposition->getBeginLoc(); }
SourceLocation getEndLoc() const { return Decomposition->getEndLoc(); }

child_range children() {
return child_range(reinterpret_cast<Stmt **>(&Index),
reinterpret_cast<Stmt **>(&Index + 1));
}

const_child_range children() const {
return const_child_range(
reinterpret_cast<Stmt **>(const_cast<Expr **>(&Index)),
reinterpret_cast<Stmt **>(const_cast<Expr **>(&Index + 1)));
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXDestructuringExpansionSelectExprClass;
}
};

} // namespace clang

#endif // LLVM_CLANG_AST_EXPRCXX_H
17 changes: 17 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,14 @@ DEF_TRAVERSE_DECL(UsingShadowDecl, {})

DEF_TRAVERSE_DECL(ConstructorUsingShadowDecl, {})

DEF_TRAVERSE_DECL(ExpansionStmtDecl, {
if (D->getInstantiations() &&
getDerived().shouldVisitTemplateInstantiations())
TRY_TO(TraverseStmt(D->getInstantiations()));

TRY_TO(TraverseStmt(D->getExpansionPattern()));
})

DEF_TRAVERSE_DECL(OMPThreadPrivateDecl, {
for (auto *I : D->varlist()) {
TRY_TO(TraverseStmt(I));
Expand Down Expand Up @@ -3117,6 +3125,15 @@ DEF_TRAVERSE_STMT(RequiresExpr, {
TRY_TO(TraverseConceptRequirement(Req));
})

DEF_TRAVERSE_STMT(CXXEnumeratingExpansionStmt, {})
DEF_TRAVERSE_STMT(CXXIteratingExpansionStmt, {})
DEF_TRAVERSE_STMT(CXXDestructuringExpansionStmt, {})
DEF_TRAVERSE_STMT(CXXDependentExpansionStmt, {})
DEF_TRAVERSE_STMT(CXXExpansionInstantiationStmt, {})
DEF_TRAVERSE_STMT(CXXExpansionInitListExpr, {})
DEF_TRAVERSE_STMT(CXXExpansionInitListSelectExpr, {})
DEF_TRAVERSE_STMT(CXXDestructuringExpansionSelectExpr, {})

// These literals (all of them) do not need any action.
DEF_TRAVERSE_STMT(IntegerLiteral, {})
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
Expand Down
Loading