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
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to