[clang] [Clang] [C++26] Implement P1306R5 Expansion Statements (PR #165195)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 26 21:01:23 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: None (Sirraide)
<details>
<summary>Changes</summary>
This adds support for [expansion statements](https://wg21.link/P1306R5) (aka `template for`) to Clang. The implementation is based in part on @<!-- -->katzdm’s fork, so I’m adding him as a co-author whenever this is merged. I’ve also gone through open core issues for this feature and integrated support for them.
The main thing that currently *isn’t* supported properly is the computation of the size of an iterating expansion statement: this requires synthesising a lambda in Sema and evaluating it, which seems rather complicated, and I’m unsure as to how to go about that, so currently, we cheat by computing `end - begin` instead. Because of this, I also haven’t added the feature test macro yet.
This is a rather large patch that introduces a new `Decl` and `DeclContext` (that being `ExpansionStmtDecl`; we need a `DeclContext` for this because the body of an expansion statement must be treated as a dependent context prior to expansion), and a number of `Stmt`s that correspond to the different kinds of expansion statements, as well as some auxiliary expressions. I considered splitting this up in some fashion to make it easier to review, but given how interconnected this all is, doing so seems non-trivial to me, so I’ve left it as one patch.
About ~4200 lines of this patch are just tests, and another large portion is just a lot of the range-based `for` loop code that was shuffled around a bit so I could reuse it for iterating expansion statements.
The memory usage of `CXXExpansionStmt` and friends could probably be optimised a bit, but I don’t think there’s an easy way to do that, and I also doubt that expansion statements are common enough to where allocating a bit more memory than strictly necessary for the unexpanded state really matters (if anything, the expansions are going to use a lot more memory anyway).
There is some weirdness with lifetime-extension of temporaries in destructuring expansion statements in codegen: I had to use *two* `LexicalScope`s to make sure the cleanup is emitted in the right place (with just one it would float up to the end of the enclosing scope); I have no idea why that happens, but it might be related to a bug in our lifetime-extension support that I discovered along the way (see #<!-- -->165182). CC @<!-- -->efriedma-quic, @<!-- -->rjmccall maybe you have an idea what might be happening there.
---
Patch is 369.68 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/165195.diff
77 Files Affected:
- (modified) clang/docs/ReleaseNotes.rst (+2)
- (modified) clang/include/clang/AST/ASTNodeTraverser.h (+6)
- (modified) clang/include/clang/AST/ComputeDependence.h (+3)
- (modified) clang/include/clang/AST/Decl.h (+3-1)
- (modified) clang/include/clang/AST/DeclBase.h (+13)
- (modified) clang/include/clang/AST/DeclTemplate.h (+47)
- (modified) clang/include/clang/AST/ExprCXX.h (+152)
- (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+16)
- (modified) clang/include/clang/AST/StmtCXX.h (+351)
- (modified) clang/include/clang/AST/TextNodeDumper.h (+7)
- (modified) clang/include/clang/Basic/DeclNodes.td (+1)
- (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+9-2)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+23-2)
- (modified) clang/include/clang/Basic/LangOptions.def (+1)
- (modified) clang/include/clang/Basic/StmtNodes.td (+13)
- (modified) clang/include/clang/Driver/Options.td (+4)
- (modified) clang/include/clang/Parse/Parser.h (+36-3)
- (modified) clang/include/clang/Sema/ScopeInfo.h (+5-1)
- (modified) clang/include/clang/Sema/Sema.h (+102-2)
- (modified) clang/include/clang/Serialization/ASTBitCodes.h (+31-10)
- (modified) clang/lib/AST/ASTImporter.cpp (+176)
- (modified) clang/lib/AST/ComputeDependence.cpp (+6)
- (modified) clang/lib/AST/DeclBase.cpp (+13-1)
- (modified) clang/lib/AST/DeclPrinter.cpp (+6)
- (modified) clang/lib/AST/DeclTemplate.cpp (+22)
- (modified) clang/lib/AST/Expr.cpp (+3)
- (modified) clang/lib/AST/ExprCXX.cpp (+59)
- (modified) clang/lib/AST/ExprClassification.cpp (+3)
- (modified) clang/lib/AST/ExprConstant.cpp (+38)
- (modified) clang/lib/AST/ItaniumMangle.cpp (+9-1)
- (modified) clang/lib/AST/StmtCXX.cpp (+148)
- (modified) clang/lib/AST/StmtPrinter.cpp (+61-1)
- (modified) clang/lib/AST/StmtProfile.cpp (+46)
- (modified) clang/lib/AST/TextNodeDumper.cpp (+22-1)
- (modified) clang/lib/CodeGen/CGDecl.cpp (+7)
- (modified) clang/lib/CodeGen/CGStmt.cpp (+46)
- (modified) clang/lib/CodeGen/CodeGenFunction.h (+2)
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+1)
- (modified) clang/lib/Frontend/FrontendActions.cpp (+2)
- (modified) clang/lib/Parse/ParseDecl.cpp (+6-31)
- (modified) clang/lib/Parse/ParseExpr.cpp (+11-2)
- (modified) clang/lib/Parse/ParseInit.cpp (+20)
- (modified) clang/lib/Parse/ParseStmt.cpp (+119-12)
- (modified) clang/lib/Sema/CMakeLists.txt (+1)
- (modified) clang/lib/Sema/Sema.cpp (+2-2)
- (modified) clang/lib/Sema/SemaDecl.cpp (+3-3)
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+3)
- (modified) clang/lib/Sema/SemaExceptionSpec.cpp (+8)
- (added) clang/lib/Sema/SemaExpand.cpp (+584)
- (modified) clang/lib/Sema/SemaExpr.cpp (+3-2)
- (modified) clang/lib/Sema/SemaExprCXX.cpp (-1)
- (modified) clang/lib/Sema/SemaLambda.cpp (+8-11)
- (modified) clang/lib/Sema/SemaLookup.cpp (+41-10)
- (modified) clang/lib/Sema/SemaStmt.cpp (+308-225)
- (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+27-3)
- (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+40)
- (modified) clang/lib/Sema/SemaTemplateVariadic.cpp (+6-2)
- (modified) clang/lib/Sema/TreeTransform.h (+232-1)
- (modified) clang/lib/Serialization/ASTCommon.cpp (+1)
- (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+12)
- (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+108)
- (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+9)
- (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+80)
- (modified) clang/lib/StaticAnalyzer/Core/ExprEngine.cpp (+8)
- (added) clang/test/AST/ast-dump-expansion-stmt.cpp (+49)
- (added) clang/test/AST/ast-print-expansion-stmts.cpp (+104)
- (added) clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp (+471)
- (added) clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp (+1518)
- (added) clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp (+429)
- (added) clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp (+208)
- (added) clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp (+474)
- (added) clang/test/Parser/cxx2c-expansion-statements.cpp (+62)
- (added) clang/test/SemaCXX/cxx2c-expansion-statements.cpp (+920)
- (added) clang/test/SemaCXX/cxx2c-fexpansion-statements.cpp (+9)
- (modified) clang/tools/libclang/CIndex.cpp (+1)
- (modified) clang/tools/libclang/CXCursor.cpp (+8)
- (modified) clang/www/cxx_status.html (+1-1)
``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e6e33e7a9a280..b247493752a7e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -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.
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index e74bb72571d64..69915800397cf 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -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) {
diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h
index c298f2620f211..792f45bea5aeb 100644
--- a/clang/include/clang/AST/ComputeDependence.h
+++ b/clang/include/clang/AST/ComputeDependence.h
@@ -94,6 +94,7 @@ class DesignatedInitExpr;
class ParenListExpr;
class PseudoObjectExpr;
class AtomicExpr;
+class CXXExpansionInitListExpr;
class ArraySectionExpr;
class OMPArrayShapingExpr;
class OMPIteratorExpr;
@@ -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);
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 406d79ebd6641..575bd4d160882 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -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;
}
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index c6326a8ba506d..00866efa4b164 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -2195,6 +2195,10 @@ class DeclContext {
return getDeclKind() == Decl::RequiresExprBody;
}
+ bool isExpansionStmt() const {
+ return getDeclKind() == Decl::ExpansionStmt;
+ }
+
bool isNamespace() const { return getDeclKind() == Decl::Namespace; }
bool isStdNamespace() const;
@@ -2292,6 +2296,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().
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index a4a1bb9c13c79..4dc7fefb686e9 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3343,6 +3343,53 @@ 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;
+
+ 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;
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index d78c7b6363b5d..5f50064d512ee 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5501,6 +5501,158 @@ 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
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 32b2b6bdb989c..33413f8a742fc 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1881,6 +1881,13 @@ 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));
@@ -3117,6 +3124,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, {})
diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index 5d68d3ef64a20..570151371e4e9 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -22,6 +22,7 @@
namespace clang {
class VarDecl;
+class ExpansionStmtDecl;
/// CXXCatchStmt - This represents a C++ catch block.
///
@@ -524,6 +525,356 @@ class CoreturnStmt : public Stmt {
}
};
+/// CXXExpansionStmt - Base class for an unexpanded C++ expansion statement.
+class CXXExpansionStmt : public Stmt {
+ friend class ASTStmtReader;
+
+ ExpansionStmtDecl *ParentDecl;
+ SourceLocation ForLoc;
+ SourceLocation LParenLoc;
+ SourceLocation ColonLoc;
+ SourceLocation RParenLoc;
+
+protected:
+ enum SubStmt {
+ INIT,
+ VAR,
+ BODY,
+ FIRST_CHILD_STMT,
+
+ // CXXDependentExpansionStmt
+ EXPANSION_INITIALIZER = FIRST_CHILD_STMT,
+ COUNT_CXXDependentExpansionStmt,
+
+ // CXXDestructuringExpansionStmt
+ DECOMP_DECL = FIRST_CHILD_STMT,
+ COUNT_CXXDestructuringExpansionStmt,
+
+ // CXXIteratingExpansionStmt
+ RANGE = FIRST_CHILD_STMT,
+ BEGIN,
+ END,
+ COUNT_CXXIteratingExpansionStmt,
+
+ MAX_COUNT = COUNT_CXXIteratingExpansionStmt,
+ };
+
+ // Managing the memory for this properly would be rather complicated, and
+ // expansion statements are fairly uncommon, so just allocate space for the
+ // maximum amount of substatements we could possibly have.
+ Stmt *SubStmts[MAX_COUNT];
+
+ CXXExpansionStmt(StmtClass SC, EmptyShell Empty);
+ CXXExpansionStmt(StmtClass SC, ExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, SourceLocation ForLoc,
+ SourceLocation LParenLoc, SourceLocation ColonLoc,
+ SourceLocation RParenLoc);
+
+public:
+ SourceLocation getForLoc() const { return ForLoc; }
+ SourceLocation getLParenLoc() const { return LParenLoc; }
+ SourceLocation getColonLoc() const { return ColonLoc; }
+ SourceLocation getRParenLoc() const { return RParenLoc; }
+
+ SourceLocation getBeginLoc() const;
+ SourceLocation getEndLoc() const {
+ return getBody() ? getBody()->getEndLoc() : RParenLoc;
+ }
+
+ bool hasDependentSize() const;
+
+ ExpansionStmtDecl* getDecl() { return ParentDecl; }
+ const ExpansionStmtDecl* getDecl() const { return ParentDecl; }
+
+ Stmt *getInit() { return SubStmts[INIT]; }
+ const Stmt *getInit() const { return SubStmts[INIT]; }
+ void setInit(Stmt* S) { SubStmts[INIT] = S; }
+
+ VarDecl *getExpansionVariable();
+ const VarDecl *getExpansionVariable() const {
+ return const_cast<CXXExpansionStmt *>(this)->getExpansionVariable();
+ }
+
+ DeclStmt *getExpansionVarStmt() { return cast<DeclStmt>(SubStmts[VAR]); }
+ const DeclStmt *getExpansionVarStmt() const {
+ return cast<DeclStmt>(SubStmts[VAR]);
+ }
+
+ void setExpansionVarStmt(Stmt* S) { SubStmts[VAR] = S; }
+
+ Stmt *getBody() { return SubStmts[BODY]; }
+ const Stmt *getBody() const { return SubStmts[BODY]; }
+ void setBody(Stmt* S) { SubStmts[BODY] = S; }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() >= firstCXXExpansionStmtConstant &&
+ T->getStmtClass() <= lastCXXExpansionStmtConstant;
+ }
+
+ child_range children() {
+ return child_range(SubStmts, SubStmts + FIRST_CHILD_STMT);
+ }
+
+ const_child_range children() const {
+ return const_child_range(SubStmts, SubStmts + FIRST_CHILD_STMT);
+ }
+};
+
+/// Represents an unexpanded enumerating expansion statement.
+///
+/// The expansion initializer of this is always a CXXExpansionInitListExpr.
+class CXXEnumeratingExpansionStmt : public CXXExpansionStmt {
+ friend class ASTStmtReader;
+
+public:
+ CXXEnumeratingExpansionStmt(EmptyShell Empty);
+ CXXEnumeratingExpansionStmt(ExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, SourceLocation ForLoc,
+ SourceLocation LParenLoc, SourceLocation ColonLoc,
+ SourceLocation RParenLoc);
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXEnumeratingExpansionStmtClass;
+ }
+};
+
+/// Represents an expansion statement whose expansion-initializer is dependent.
+class CXXDependentExpansionStmt : public CXXExpansionStmt {
+ friend class ASTStmtReader;
+
+public:
+ CXXDependentExpansionStmt(EmptyShell Empty);
+ CXXDependentExpansionStmt(ExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, Expr *ExpansionInitializer,
+ SourceLocation ForLoc, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+ Expr *getExpansionInitializer() {
+ return cast<Expr>(SubStmts[EXPANSION_INITIALIZER]);
+ }
+ const Expr *getExpansionInitializer() const {
+ return cast<Expr>(SubStmts[EXPANSION_INITIALIZER]);
+ }
+ void setExpansionInitializer(Expr *S) { SubStmts[EXPANSION_INITIALIZER] = S; }
+
+ child_range children() {
+ return child_range(SubStmts, SubStmts + COUNT_CXXDependentExpansionStmt);
+ }
+
+ const_child_range children() const {
+ return const_child_range(SubStmts,
+ SubStmts + COUNT_CXXDependentExpansionStmt);
+ }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXDependentExpansionStmtClass;
+ }
+};
+
+/// Represents an unexpanded iterating expansion statement.
+///
+/// The expression used to compute the size of the expansion is not stored in
+/// this as it is only created at the moment of expansion.
+class CXXIteratingExpansionStmt : public CXXExpansionStmt {
+ friend class ASTStmtReader;
+
+public:
+ CXXIteratingExpansionStmt(EmptyShell Empty);
+ CXXIteratingExpansionStmt(ExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, DeclStmt *Range,
+ DeclStmt *Begin, DeclStmt *End,
+ SourceLocation ForLoc, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+ const DeclStmt *getRangeVarStmt() const {
+ return cast<DeclStmt>(SubStmts[RANGE]);
+ }
+ DeclStmt *getRangeVarStmt() { return cast<DeclStmt>(SubStmts[RANGE]); }
+ void setRangeVarStmt(DeclStmt *S) { SubStmts[RANGE] = S; }
+
+ const VarDecl *getRangeVar() const {
+ return cast<VarDecl>(getRangeVarStmt()->getSingleDecl());
+ }
+
+ VarDecl *getRangeVar() {
+ return cast<VarDecl>(getRangeVarStmt()->getSingleDecl());
+ }
+
+ const DeclStmt *getBeginVarStmt() const {
+ return cast<DeclStmt>(SubStmts[BEGIN]);
+ }
+ DeclStmt *getBeginVarStmt() { return cast<DeclStmt>(SubStmts[BEGIN]); }
+ void setBeginVarStmt(DeclStmt *S) { SubStmts[BEGIN] = S; }
+
+ const VarDecl *getBeginVar() const {
+ return cast<VarDecl>(getBeginVarStmt()->getSingleDecl());
+ }
+
+ VarDecl *getBeginVar() {
+ return cast<VarDecl>(getBeginVarStmt()->get...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/165195
More information about the cfe-commits
mailing list