[clang] [Clang] [C++26] Expansion Statements (Part 1: AST) (PR #169680)

via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 3 13:23:22 PST 2025


https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/169680

>From e5f2dd466d5126594a159fc37a96ba6b54978e26 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 25 Nov 2025 17:18:05 +0100
Subject: [PATCH 1/9] [Clang] [C++26] Expansion Statements (Part 1)

---
 clang/include/clang/AST/ASTNodeTraverser.h    |   6 +
 clang/include/clang/AST/ComputeDependence.h   |   3 +
 clang/include/clang/AST/Decl.h                |   4 +-
 clang/include/clang/AST/DeclBase.h            |  13 +
 clang/include/clang/AST/DeclTemplate.h        | 114 +++++
 clang/include/clang/AST/ExprCXX.h             | 159 ++++++
 clang/include/clang/AST/RecursiveASTVisitor.h |  17 +
 clang/include/clang/AST/StmtCXX.h             | 478 ++++++++++++++++++
 clang/include/clang/AST/TextNodeDumper.h      |   5 +
 clang/include/clang/Basic/DeclNodes.td        |   1 +
 clang/include/clang/Basic/StmtNodes.td        |  14 +
 .../include/clang/Serialization/ASTBitCodes.h |  41 +-
 clang/lib/AST/ASTImporter.cpp                 | 174 +++++++
 clang/lib/AST/ComputeDependence.cpp           |   7 +
 clang/lib/AST/DeclBase.cpp                    |  14 +-
 clang/lib/AST/DeclPrinter.cpp                 |   6 +
 clang/lib/AST/DeclTemplate.cpp                |  23 +
 clang/lib/AST/Expr.cpp                        |   3 +
 clang/lib/AST/ExprCXX.cpp                     |  58 +++
 clang/lib/AST/ExprClassification.cpp          |   3 +
 clang/lib/AST/ExprConstant.cpp                |   3 +
 clang/lib/AST/ItaniumMangle.cpp               |  10 +-
 clang/lib/AST/StmtCXX.cpp                     | 153 ++++++
 clang/lib/AST/StmtPrinter.cpp                 |  62 ++-
 clang/lib/AST/StmtProfile.cpp                 |  47 ++
 clang/lib/AST/TextNodeDumper.cpp              |  23 +-
 clang/lib/Sema/Sema.cpp                       |   7 +-
 clang/lib/Sema/SemaDecl.cpp                   |   4 +-
 clang/lib/Sema/SemaExceptionSpec.cpp          |   8 +
 clang/lib/Sema/SemaExpr.cpp                   |   5 +-
 clang/lib/Sema/SemaExprCXX.cpp                |   1 -
 clang/lib/Sema/SemaLambda.cpp                 |  28 +-
 clang/lib/Sema/SemaLookup.cpp                 |   4 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   5 +
 clang/lib/Sema/TreeTransform.h                |  53 ++
 clang/lib/Serialization/ASTCommon.cpp         |   1 +
 clang/lib/Serialization/ASTReaderDecl.cpp     |  12 +
 clang/lib/Serialization/ASTReaderStmt.cpp     | 107 ++++
 clang/lib/Serialization/ASTWriterDecl.cpp     |   9 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |  79 +++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |   8 +
 clang/tools/libclang/CIndex.cpp               |   1 +
 clang/tools/libclang/CXCursor.cpp             |   8 +
 43 files changed, 1744 insertions(+), 37 deletions(-)

diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index e74bb72571d64..fd64d86f83bfd 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -959,6 +959,12 @@ class ASTNodeTraverser
     }
   }
 
+  void VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *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 ee2321dd158d4..b8f8e002ebcce 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 5519787d71f88..71e6898f4c94d 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::CXXExpansionStmt;
+  }
+
   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..45a58b6589074 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3343,6 +3343,120 @@ 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.
+///
+/// For the remainder of this comment, let 'expanding' an expansion statement
+/// refer to the process of performing template substitution on its body N
+/// times, where N is the expansion size (how this size is determined depends on
+/// the kind of expansion statement); by contrast we may sometimes 'instantiate'
+/// an expansion statement (because it happens to be in a template). This is
+/// just regular template instantiation.
+///
+/// Apart from a template parameter list that contains a template parameter used
+/// as the expansion index, this node contains a 'CXXExpansionStmtPattern' as
+/// well as a 'CXXExpansionStmtInstantiation'. These two members correspond to
+/// distinct representations of the expansion statement: the former is used
+/// prior to expansion and contains all the parts needed to perform expansion;
+/// the latter holds the expanded/desugared AST nodes that result from the
+/// expansion.
+///
+/// After expansion, the 'CXXExpansionStmtPattern' is no longer updated and left
+/// as-is; this also means that, if an already-expanded expansion statement is
+/// inside a template, and that template is then instantiated, the
+/// 'CXXExpansionStmtPattern' is *not* instantiated; only the
+/// 'CXXExpansionStmtInstantiation' is. The latter is also what's used for
+/// codegen and constant evaluation.
+///
+/// For example, if the user writes the following expansion statement:
+/// \verbatim
+///   std::tuple<int, int, int> a{1, 2, 3};
+///   template for (auto x : a) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// The 'CXXExpansionStmtPattern' of this particular 'CXXExpansionStmtDecl' is a
+/// 'CXXDestructuringExpansionStmtPattern', which stores, amongst other things,
+/// the declaration of the variable 'x' as well as the expansion-initializer
+/// 'a'.
+///
+/// After expansion, we end up with a 'CXXExpansionStmtInstantiation' that
+/// contains a DecompositionDecl and 3 CompoundStmts, one for each expansion:
+///
+/// \verbatim
+/// {
+///   auto [__u0, __u1, __u2] = a;
+///   {
+///     auto x = __u0;
+///     // ...
+///   }
+///   {
+///     auto x = __u1;
+///     // ...
+///   }
+///   {
+///     auto x = __u2;
+///     // ...
+///   }
+/// }
+/// \endverbatim
+///
+/// The outer braces shown above are implicit; we don't actually create another
+/// CompoundStmt wrapping everything.
+///
+/// \see CXXExpansionStmtPattern
+/// \see CXXExpansionStmtInstantiation
+class CXXExpansionStmtDecl : public Decl, public DeclContext {
+  CXXExpansionStmtPattern *Expansion = nullptr;
+  TemplateParameterList *TParams;
+  CXXExpansionStmtInstantiation *Instantiations = nullptr;
+
+  CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
+                       TemplateParameterList *TParams);
+
+public:
+  friend class ASTDeclReader;
+
+  static CXXExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC,
+                                      SourceLocation Loc,
+                                      TemplateParameterList *TParams);
+  static CXXExpansionStmtDecl *CreateDeserialized(ASTContext &C,
+                                                  GlobalDeclID ID);
+
+  CXXExpansionStmtPattern *getExpansionPattern() { return Expansion; }
+  const CXXExpansionStmtPattern *getExpansionPattern() const {
+    return Expansion;
+  }
+  void setExpansionPattern(CXXExpansionStmtPattern *S) { Expansion = S; }
+
+  CXXExpansionStmtInstantiation *getInstantiations() { return Instantiations; }
+  const CXXExpansionStmtInstantiation *getInstantiations() const {
+    return Instantiations;
+  }
+
+  void setInstantiations(CXXExpansionStmtInstantiation *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 == CXXExpansionStmt; }
+};
+
 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 9435ab069a520..668a51dad0ae9 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5499,6 +5499,165 @@ class BuiltinBitCastExpr final
   }
 };
 
+/// Represents an expansion-init-list of an enumerating expansion statement.
+///
+/// \see CXXEnumeratingExpansionStmtPattern
+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.
+///
+/// \see CXXEnumeratingExpansionStmtPattern
+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;
+  }
+};
+
+/// This class serves the same purpose as CXXExpansionInitListSelectExpr, but
+/// for destructuring expansion statements; that is, instead of selecting among
+/// a list of expressions, it selects from a list of 'BindingDecl's.
+///
+/// \see CXXEnumeratingExpansionStmtPattern
+/// \see CXXDestructuringExpansionStmtPattern
+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 8f427427d71ed..24052df70c7a8 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1881,6 +1881,14 @@ DEF_TRAVERSE_DECL(UsingShadowDecl, {})
 
 DEF_TRAVERSE_DECL(ConstructorUsingShadowDecl, {})
 
+DEF_TRAVERSE_DECL(CXXExpansionStmtDecl, {
+  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 +3125,15 @@ DEF_TRAVERSE_STMT(RequiresExpr, {
     TRY_TO(TraverseConceptRequirement(Req));
 })
 
+DEF_TRAVERSE_STMT(CXXEnumeratingExpansionStmtPattern, {})
+DEF_TRAVERSE_STMT(CXXIteratingExpansionStmtPattern, {})
+DEF_TRAVERSE_STMT(CXXDestructuringExpansionStmtPattern, {})
+DEF_TRAVERSE_STMT(CXXDependentExpansionStmtPattern, {})
+DEF_TRAVERSE_STMT(CXXExpansionStmtInstantiation, {})
+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..96c3f912d6c3e 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -22,6 +22,7 @@
 namespace clang {
 
 class VarDecl;
+class CXXExpansionStmtDecl;
 
 /// CXXCatchStmt - This represents a C++ catch block.
 ///
@@ -524,6 +525,483 @@ class CoreturnStmt : public Stmt {
   }
 };
 
+/// CXXExpansionStmtPattern - Base class for an unexpanded C++ expansion
+/// statement.
+///
+/// The main purpose for this class is to store the AST nodes common to all
+/// variants of expansion statements; it also provides storage for additional
+/// subexpressions required by its derived classes. This is to simplify the
+/// implementation of 'children()' and friends.
+///
+/// \see CXXExpansionStmtDecl
+/// \see CXXEnumeratingExpansionStmtPattern
+/// \see CXXIteratingExpansionStmtPattern
+/// \see CXXDestructuringExpansionStmtPattern
+/// \see CXXDependentExpansionStmtPattern
+class CXXExpansionStmtPattern : public Stmt {
+  friend class ASTStmtReader;
+
+  CXXExpansionStmtDecl *ParentDecl;
+  SourceLocation LParenLoc;
+  SourceLocation ColonLoc;
+  SourceLocation RParenLoc;
+
+protected:
+  enum SubStmt {
+    INIT,
+    VAR,
+    BODY,
+    FIRST_CHILD_STMT,
+
+    // CXXDependentExpansionStmtPattern
+    EXPANSION_INITIALIZER = FIRST_CHILD_STMT,
+    COUNT_CXXDependentExpansionStmtPattern,
+
+    // CXXDestructuringExpansionStmtPattern
+    DECOMP_DECL = FIRST_CHILD_STMT,
+    COUNT_CXXDestructuringExpansionStmtPattern,
+
+    // CXXIteratingExpansionStmtPattern
+    RANGE = FIRST_CHILD_STMT,
+    BEGIN,
+    END,
+    COUNT_CXXIteratingExpansionStmtPattern,
+
+    MAX_COUNT = COUNT_CXXIteratingExpansionStmtPattern,
+  };
+
+  // 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];
+
+  CXXExpansionStmtPattern(StmtClass SC, EmptyShell Empty);
+  CXXExpansionStmtPattern(StmtClass SC, CXXExpansionStmtDecl *ESD, Stmt *Init,
+                          DeclStmt *ExpansionVar, SourceLocation LParenLoc,
+                          SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+public:
+  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;
+
+  CXXExpansionStmtDecl *getDecl() { return ParentDecl; }
+  const CXXExpansionStmtDecl *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<CXXExpansionStmtPattern *>(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() >= firstCXXExpansionStmtPatternConstant &&
+           T->getStmtClass() <= lastCXXExpansionStmtPatternConstant;
+  }
+
+  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.
+///
+/// An 'enumerating' expansion statement is one whose expansion-initializer
+/// is a brace-enclosed expression-list; this list is syntactically similar to
+/// an initializer list, but it isn't actually an expression in and of itself
+/// (in that it is never evaluated or emitted) and instead is just treated as
+/// a group of expressions. The expansion initializer of this is always a
+/// 'CXXExpansionInitListExpr'.
+///
+/// Example:
+/// \verbatim
+///   template for (auto x : { 1, 2, 3 }) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// Note that the expression-list may also contain pack expansions, e.g.
+/// '{ 1, xs... }', in which case the expansion size is dependent.
+///
+/// Here, the '{ 1, 2, 3 }' is parsed as a 'CXXExpansionInitListExpr'. This node
+/// handles storing (and pack-expanding) the individual expressions.
+///
+/// Sema then wraps this with a 'CXXExpansionInitListSelectExpr', which also
+/// contains a reference to an integral NTTP that is used as the expansion
+/// index; this index is either dependent (if the expansion-size is dependent),
+/// or set to a value of I in the I-th expansion during the expansion process.
+///
+/// The actual expansion is done by 'BuildCXXExpansionInitListSelectExpr()': for
+/// example, during the 2nd expansion of '{ a, b, c }', I is equal to 1, and
+/// BuildCXXExpansionInitListSelectExpr(), when called via TreeTransform,
+/// 'instantiates' the expression '{ a, b, c }' to just 'b'.
+class CXXEnumeratingExpansionStmtPattern : public CXXExpansionStmtPattern {
+  friend class ASTStmtReader;
+
+public:
+  CXXEnumeratingExpansionStmtPattern(EmptyShell Empty);
+  CXXEnumeratingExpansionStmtPattern(CXXExpansionStmtDecl *ESD, Stmt *Init,
+                                     DeclStmt *ExpansionVar,
+                                     SourceLocation LParenLoc,
+                                     SourceLocation ColonLoc,
+                                     SourceLocation RParenLoc);
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXEnumeratingExpansionStmtPatternClass;
+  }
+};
+
+/// Represents an expansion statement whose expansion-initializer is
+/// type-dependent.
+///
+/// This will be instantiated as either a 'CXXIteratingExpansionStmtPattern' or
+/// a 'CXXDestructuringExpansionStmtPattern'. Dependent expansion statements can
+/// never be enumerating; those are always stored as a
+/// 'CXXEnumeratingExpansionStmtPattern', even if the expansion size is
+/// dependent because the expression-list contains a pack.
+///
+/// Example:
+/// \verbatim
+///   template <typename T>
+///   void f() {
+///     template for (auto x : T()) {
+///       // ...
+///     }
+///   }
+/// \endverbatim
+class CXXDependentExpansionStmtPattern : public CXXExpansionStmtPattern {
+  friend class ASTStmtReader;
+
+public:
+  CXXDependentExpansionStmtPattern(EmptyShell Empty);
+  CXXDependentExpansionStmtPattern(CXXExpansionStmtDecl *ESD, Stmt *Init,
+                                   DeclStmt *ExpansionVar,
+                                   Expr *ExpansionInitializer,
+                                   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_CXXDependentExpansionStmtPattern);
+  }
+
+  const_child_range children() const {
+    return const_child_range(SubStmts,
+                             SubStmts + COUNT_CXXDependentExpansionStmtPattern);
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXDependentExpansionStmtPatternClass;
+  }
+};
+
+/// Represents an unexpanded iterating expansion statement.
+///
+/// An 'iterating' expansion statement is one whose expansion-initializer is a
+/// a range (i.e. it has a corresponding 'begin()'/'end()' pair that is
+/// determined based on a number of conditions as stated in [stmt.expand] and
+/// [stmt.ranged]).
+///
+/// The expression used to compute the size of the expansion is not stored and
+/// is only created at the moment of expansion.
+///
+/// Example:
+/// \verbatim
+///   static constexpr std::string_view foo = "1234";
+///   template for (auto x : foo) {
+///     // ...
+///   }
+/// \endverbatim
+class CXXIteratingExpansionStmtPattern : public CXXExpansionStmtPattern {
+  friend class ASTStmtReader;
+
+public:
+  CXXIteratingExpansionStmtPattern(EmptyShell Empty);
+  CXXIteratingExpansionStmtPattern(CXXExpansionStmtDecl *ESD, Stmt *Init,
+                                   DeclStmt *ExpansionVar, DeclStmt *Range,
+                                   DeclStmt *Begin, DeclStmt *End,
+                                   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()->getSingleDecl());
+  }
+
+  const DeclStmt *getEndVarStmt() const {
+    return cast<DeclStmt>(SubStmts[END]);
+  }
+  DeclStmt *getEndVarStmt() { return cast<DeclStmt>(SubStmts[END]); }
+  void setEndVarStmt(DeclStmt *S) { SubStmts[END] = S; }
+
+  const VarDecl *getEndVar() const {
+    return cast<VarDecl>(getEndVarStmt()->getSingleDecl());
+  }
+
+  VarDecl *getEndVar() {
+    return cast<VarDecl>(getEndVarStmt()->getSingleDecl());
+  }
+
+  child_range children() {
+    return child_range(SubStmts,
+                       SubStmts + COUNT_CXXIteratingExpansionStmtPattern);
+  }
+
+  const_child_range children() const {
+    return const_child_range(SubStmts,
+                             SubStmts + COUNT_CXXIteratingExpansionStmtPattern);
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXIteratingExpansionStmtPatternClass;
+  }
+};
+
+/// Represents an unexpanded destructuring expansion statement.
+///
+/// A 'destructuring' expansion statement is any expansion statement that is
+/// not enumerating or iterating (i.e. destructuring is the last thing we try,
+/// and if it doesn't work, the program is ill-formed).
+///
+/// This essentially involves treating the expansion-initializer as the
+/// initializer of a structured-binding declarations, with the number of
+/// bindings and expansion size determined by the usual means (array size,
+/// std::tuple_size, etc.).
+///
+/// Example:
+/// \verbatim
+///   std::array<int, 3> a {1, 2, 3};
+///   template for (auto x : a) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// Sema wraps the initializer with a CXXDestructuringExpansionSelectExpr, which
+/// selects a binding based on the current expansion index; this is analogous to
+/// how 'CXXExpansionInitListSelectExpr' is used; see the documentation of
+/// 'CXXEnumeratingExpansionStmtPattern' for more details on this.
+class CXXDestructuringExpansionStmtPattern : public CXXExpansionStmtPattern {
+  friend class ASTStmtReader;
+
+public:
+  CXXDestructuringExpansionStmtPattern(EmptyShell Empty);
+  CXXDestructuringExpansionStmtPattern(CXXExpansionStmtDecl *ESD, Stmt *Init,
+                                       DeclStmt *ExpansionVar,
+                                       Stmt *DecompositionDeclStmt,
+                                       SourceLocation LParenLoc,
+                                       SourceLocation ColonLoc,
+                                       SourceLocation RParenLoc);
+
+  Stmt *getDecompositionDeclStmt() { return SubStmts[DECOMP_DECL]; }
+  const Stmt *getDecompositionDeclStmt() const { return SubStmts[DECOMP_DECL]; }
+  void setDecompositionDeclStmt(Stmt *S) { SubStmts[DECOMP_DECL] = S; }
+
+  DecompositionDecl *getDecompositionDecl();
+  const DecompositionDecl *getDecompositionDecl() const {
+    return const_cast<CXXDestructuringExpansionStmtPattern *>(this)
+        ->getDecompositionDecl();
+  }
+
+  child_range children() {
+    return child_range(SubStmts,
+                       SubStmts + COUNT_CXXDestructuringExpansionStmtPattern);
+  }
+
+  const_child_range children() const {
+    return const_child_range(
+        SubStmts, SubStmts + COUNT_CXXDestructuringExpansionStmtPattern);
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXDestructuringExpansionStmtPatternClass;
+  }
+};
+
+/// Represents the code generated for an expanded expansion statement.
+///
+/// This holds 'shared statements' and 'instantiations'; these encode the
+/// general underlying pattern that all expansion statements desugar to:
+///
+/// \verbatim
+/// {
+///   <shared statements>
+///   {
+///     <1st instantiation>
+///   }
+///   ...
+///   {
+///     <n-th instantiation>
+///   }
+/// }
+/// \endverbatim
+///
+/// Here, the only thing that is stored in the AST are the 'shared statements'
+/// and the 'CompoundStmt's that wrap the 'instantiations'. The outer braces
+/// shown above are implicit.
+///
+/// For example, the CXXExpansionStmtInstantiation that corresponds to the
+/// following expansion statement
+///
+/// \verbatim
+///   std::tuple<int, int, int> a{1, 2, 3};
+///   template for (auto x : a) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// would be
+///
+/// \verbatim
+/// {
+///   auto [__u0, __u1, __u2] = a;
+///   {
+///     auto x = __u0;
+///     // ...
+///   }
+///   {
+///     auto x = __u1;
+///     // ...
+///   }
+///   {
+///     auto x = __u2;
+///     // ...
+///   }
+/// }
+/// \endverbatim
+class CXXExpansionStmtInstantiation final
+    : public Stmt,
+      llvm::TrailingObjects<CXXExpansionStmtInstantiation, Stmt *> {
+  friend class ASTStmtReader;
+  friend TrailingObjects;
+
+  SourceLocation BeginLoc;
+  SourceLocation EndLoc;
+
+  // Instantiations are stored first, then shared statements.
+  const unsigned NumInstantiations : 20;
+  const unsigned NumSharedStmts : 3;
+  unsigned ShouldApplyLifetimeExtensionToSharedStmts : 1;
+
+  CXXExpansionStmtInstantiation(EmptyShell Empty, unsigned NumInstantiations,
+                                unsigned NumSharedStmts);
+  CXXExpansionStmtInstantiation(SourceLocation BeginLoc, SourceLocation EndLoc,
+                                ArrayRef<Stmt *> Instantiations,
+                                ArrayRef<Stmt *> SharedStmts,
+                                bool ShouldApplyLifetimeExtensionToSharedStmts);
+
+public:
+  static CXXExpansionStmtInstantiation *
+  Create(ASTContext &C, SourceLocation BeginLoc, SourceLocation EndLoc,
+         ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> SharedStmts,
+         bool ShouldApplyLifetimeExtensionToSharedStmts);
+
+  static CXXExpansionStmtInstantiation *CreateEmpty(ASTContext &C,
+                                                    EmptyShell Empty,
+                                                    unsigned NumInstantiations,
+                                                    unsigned NumSharedStmts);
+
+  ArrayRef<Stmt *> getAllSubStmts() const {
+    return getTrailingObjects(getNumSubStmts());
+  }
+
+  MutableArrayRef<Stmt *> getAllSubStmts() {
+    return getTrailingObjects(getNumSubStmts());
+  }
+
+  unsigned getNumSubStmts() const { return NumInstantiations + NumSharedStmts; }
+
+  ArrayRef<Stmt *> getInstantiations() const {
+    return getTrailingObjects(NumInstantiations);
+  }
+
+  ArrayRef<Stmt *> getSharedStmts() const {
+    return getAllSubStmts().drop_front(NumInstantiations);
+  }
+
+  bool shouldApplyLifetimeExtensionToSharedStmts() const {
+    return ShouldApplyLifetimeExtensionToSharedStmts;
+  }
+
+  void setShouldApplyLifetimeExtensionToSharedStmts(bool Apply) {
+    ShouldApplyLifetimeExtensionToSharedStmts = Apply;
+  }
+
+  SourceLocation getBeginLoc() const { return BeginLoc; }
+  SourceLocation getEndLoc() const { return EndLoc; }
+
+  child_range children() {
+    Stmt **S = getTrailingObjects();
+    return child_range(S, S + getNumSubStmts());
+  }
+
+  const_child_range children() const {
+    Stmt *const *S = getTrailingObjects();
+    return const_child_range(S, S + getNumSubStmts());
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXExpansionStmtInstantiationClass;
+  }
+};
+
 }  // end namespace clang
 
 #endif
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 88ecd526e3d7e..3db8ce0d5aed3 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -266,6 +266,8 @@ class TextNodeDumper
   void VisitCoawaitExpr(const CoawaitExpr *Node);
   void VisitCoreturnStmt(const CoreturnStmt *Node);
   void VisitCompoundStmt(const CompoundStmt *Node);
+  void
+  VisitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *Node);
   void VisitConstantExpr(const ConstantExpr *Node);
   void VisitCallExpr(const CallExpr *Node);
   void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Node);
@@ -310,6 +312,9 @@ class TextNodeDumper
   void VisitSizeOfPackExpr(const SizeOfPackExpr *Node);
   void
   VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *Node);
+  void VisitCXXDestructuringExpansionSelectExpr(
+      const CXXDestructuringExpansionSelectExpr *Node);
+  void VisitCXXExpansionInitListExpr(const CXXExpansionInitListExpr *Node);
   void VisitObjCAtCatchStmt(const ObjCAtCatchStmt *Node);
   void VisitObjCEncodeExpr(const ObjCEncodeExpr *Node);
   void VisitObjCMessageExpr(const ObjCMessageExpr *Node);
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 04311055bb600..23f8e47939bdb 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -101,6 +101,7 @@ def AccessSpec : DeclNode<Decl>;
 def Friend : DeclNode<Decl>;
 def FriendTemplate : DeclNode<Decl>;
 def StaticAssert : DeclNode<Decl>;
+def CXXExpansionStmt : DeclNode<Decl>, DeclContext;
 def Block : DeclNode<Decl, "blocks">, DeclContext;
 def OutlinedFunction : DeclNode<Decl>, DeclContext;
 def Captured : DeclNode<Decl>, DeclContext;
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index bf3686bb372d5..505ed481e0895 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -58,6 +58,15 @@ def CXXForRangeStmt : StmtNode<Stmt>;
 def CoroutineBodyStmt : StmtNode<Stmt>;
 def CoreturnStmt : StmtNode<Stmt>;
 
+// C++ expansion statements (P1306)
+def CXXExpansionStmtPattern : StmtNode<Stmt, 1>;
+def CXXEnumeratingExpansionStmtPattern : StmtNode<CXXExpansionStmtPattern>;
+def CXXIteratingExpansionStmtPattern : StmtNode<CXXExpansionStmtPattern>;
+def CXXDestructuringExpansionStmtPattern : StmtNode<CXXExpansionStmtPattern>;
+def CXXDependentExpansionStmtPattern : StmtNode<CXXExpansionStmtPattern>;
+def CXXExpansionStmtInstantiation
+    : StmtNode<Stmt>; // *Not* derived from CXXExpansionStmtPattern!
+
 // Expressions
 def Expr : StmtNode<ValueStmt, 1>;
 def PredefinedExpr : StmtNode<Expr>;
@@ -177,6 +186,11 @@ def CoyieldExpr : StmtNode<CoroutineSuspendExpr>;
 def ConceptSpecializationExpr : StmtNode<Expr>;
 def RequiresExpr : StmtNode<Expr>;
 
+// C++26 Expansion statement support expressions
+def CXXExpansionInitListExpr : StmtNode<Expr>;
+def CXXExpansionInitListSelectExpr : StmtNode<Expr>;
+def CXXDestructuringExpansionSelectExpr : StmtNode<Expr>;
+
 // Obj-C Expressions.
 def ObjCStringLiteral : StmtNode<Expr>;
 def ObjCBoxedExpr : StmtNode<Expr>;
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index d7d429eacd67a..36cccdcbd306e 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1460,6 +1460,9 @@ enum DeclCode {
   /// \brief A StaticAssertDecl record.
   DECL_STATIC_ASSERT,
 
+  /// A C++ expansion statement.
+  DECL_EXPANSION_STMT,
+
   /// A record containing CXXBaseSpecifiers.
   DECL_CXX_BASE_SPECIFIERS,
 
@@ -1833,6 +1836,21 @@ enum StmtCode {
 
   STMT_CXX_FOR_RANGE,
 
+  /// A CXXEnumeratedExpansionStmt.
+  STMT_CXX_ENUMERATING_EXPANSION,
+
+  /// A CXXIteratingExpansionStmtPattern.
+  STMT_CXX_ITERATING_EXPANSION,
+
+  /// A CXXDestructuringExpansionStmtPattern.
+  STMT_CXX_DESTRUCTURING_EXPANSION,
+
+  /// A CXXDependentExpansionStmtPattern,
+  STMT_CXX_DEPENDENT_EXPANSION,
+
+  /// A CXXExpansionStmtInstantiation.
+  STMT_CXX_EXPANSION_INSTANTIATION,
+
   /// A CXXOperatorCallExpr record.
   EXPR_CXX_OPERATOR_CALL,
 
@@ -1914,16 +1932,19 @@ enum StmtCode {
   EXPR_TYPE_TRAIT,                  // TypeTraitExpr
   EXPR_ARRAY_TYPE_TRAIT,            // ArrayTypeTraitIntExpr
 
-  EXPR_PACK_EXPANSION,                    // PackExpansionExpr
-  EXPR_PACK_INDEXING,                     // PackIndexingExpr
-  EXPR_SIZEOF_PACK,                       // SizeOfPackExpr
-  EXPR_SUBST_NON_TYPE_TEMPLATE_PARM,      // SubstNonTypeTemplateParmExpr
-  EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr
-  EXPR_FUNCTION_PARM_PACK,                // FunctionParmPackExpr
-  EXPR_MATERIALIZE_TEMPORARY,             // MaterializeTemporaryExpr
-  EXPR_CXX_FOLD,                          // CXXFoldExpr
-  EXPR_CONCEPT_SPECIALIZATION,            // ConceptSpecializationExpr
-  EXPR_REQUIRES,                          // RequiresExpr
+  EXPR_PACK_EXPANSION,                     // PackExpansionExpr
+  EXPR_PACK_INDEXING,                      // PackIndexingExpr
+  EXPR_SIZEOF_PACK,                        // SizeOfPackExpr
+  EXPR_SUBST_NON_TYPE_TEMPLATE_PARM,       // SubstNonTypeTemplateParmExpr
+  EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,  // SubstNonTypeTemplateParmPackExpr
+  EXPR_FUNCTION_PARM_PACK,                 // FunctionParmPackExpr
+  EXPR_MATERIALIZE_TEMPORARY,              // MaterializeTemporaryExpr
+  EXPR_CXX_FOLD,                           // CXXFoldExpr
+  EXPR_CONCEPT_SPECIALIZATION,             // ConceptSpecializationExpr
+  EXPR_REQUIRES,                           // RequiresExpr
+  EXPR_CXX_EXPANSION_INIT_LIST,            // CXXExpansionInitListExpr
+  EXPR_CXX_EXPANSION_INIT_LIST_SELECT,     // CXXExpansionInitListSelectExpr
+  EXPR_CXX_DESTRUCTURING_EXPANSION_SELECT, // CXXDestructuringExpansionSelectExpr
 
   // CUDA
   EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index c1441744c8578..b94c18e60ba58 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -516,6 +516,7 @@ namespace clang {
     ExpectedDecl VisitEmptyDecl(EmptyDecl *D);
     ExpectedDecl VisitAccessSpecDecl(AccessSpecDecl *D);
     ExpectedDecl VisitStaticAssertDecl(StaticAssertDecl *D);
+    ExpectedDecl VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D);
     ExpectedDecl VisitTranslationUnitDecl(TranslationUnitDecl *D);
     ExpectedDecl VisitBindingDecl(BindingDecl *D);
     ExpectedDecl VisitNamespaceDecl(NamespaceDecl *D);
@@ -608,6 +609,16 @@ namespace clang {
     ExpectedStmt VisitCXXCatchStmt(CXXCatchStmt *S);
     ExpectedStmt VisitCXXTryStmt(CXXTryStmt *S);
     ExpectedStmt VisitCXXForRangeStmt(CXXForRangeStmt *S);
+    ExpectedStmt VisitCXXEnumeratingExpansionStmtPattern(
+        CXXEnumeratingExpansionStmtPattern *S);
+    ExpectedStmt
+    VisitCXXIteratingExpansionStmtPattern(CXXIteratingExpansionStmtPattern *S);
+    ExpectedStmt VisitCXXDestructuringExpansionStmtPattern(
+        CXXDestructuringExpansionStmtPattern *S);
+    ExpectedStmt
+    VisitCXXDependentExpansionStmtPattern(CXXDependentExpansionStmtPattern *S);
+    ExpectedStmt
+    VisitCXXExpansionStmtInstantiation(CXXExpansionStmtInstantiation *S);
     // FIXME: MSDependentExistsStmt
     ExpectedStmt VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
     ExpectedStmt VisitObjCAtCatchStmt(ObjCAtCatchStmt *S);
@@ -700,6 +711,11 @@ namespace clang {
     VisitSubstNonTypeTemplateParmPackExpr(SubstNonTypeTemplateParmPackExpr *E);
     ExpectedStmt VisitPseudoObjectExpr(PseudoObjectExpr *E);
     ExpectedStmt VisitCXXParenListInitExpr(CXXParenListInitExpr *E);
+    ExpectedStmt VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E);
+    ExpectedStmt
+    VisitCXXExpansionInitListSelectExpr(CXXExpansionInitListSelectExpr *E);
+    ExpectedStmt VisitCXXDestructuringExpansionSelectExpr(
+        CXXDestructuringExpansionSelectExpr *E);
 
     // Helper for chaining together multiple imports. If an error is detected,
     // subsequent imports will return default constructed nodes, so that failure
@@ -2852,6 +2868,34 @@ ExpectedDecl ASTNodeImporter::VisitStaticAssertDecl(StaticAssertDecl *D) {
   return ToD;
 }
 
+ExpectedDecl
+ASTNodeImporter::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
+  auto DCOrErr = Importer.ImportContext(D->getDeclContext());
+  if (!DCOrErr)
+    return DCOrErr.takeError();
+  DeclContext *DC = *DCOrErr;
+  DeclContext *LexicalDC = DC;
+
+  Error Err = Error::success();
+  auto ToLocation = importChecked(Err, D->getLocation());
+  auto ToExpansion = importChecked(Err, D->getExpansionPattern());
+  auto ToTemplateParams = importChecked(Err, D->getTemplateParameters());
+  auto ToInstantiations = importChecked(Err, D->getInstantiations());
+  if (Err)
+    return std::move(Err);
+
+  CXXExpansionStmtDecl *ToD;
+  if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, ToLocation,
+                              ToTemplateParams))
+    return ToD;
+
+  ToD->setExpansionPattern(ToExpansion);
+  ToD->setInstantiations(ToInstantiations);
+  ToD->setLexicalDeclContext(LexicalDC);
+  LexicalDC->addDeclInternal(ToD);
+  return ToD;
+}
+
 ExpectedDecl ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) {
   // Import the major distinguishing characteristics of this namespace.
   DeclContext *DC, *LexicalDC;
@@ -7450,6 +7494,96 @@ ExpectedStmt ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
       ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc);
 }
 
+ExpectedStmt ASTNodeImporter::VisitCXXEnumeratingExpansionStmtPattern(
+    CXXEnumeratingExpansionStmtPattern *S) {
+  Error Err = Error::success();
+  auto ToESD = importChecked(Err, S->getDecl());
+  auto ToInit = importChecked(Err, S->getInit());
+  auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt());
+  auto ToLParenLoc = importChecked(Err, S->getLParenLoc());
+  auto ToColonLoc = importChecked(Err, S->getColonLoc());
+  auto ToRParenLoc = importChecked(Err, S->getRParenLoc());
+  if (Err)
+    return std::move(Err);
+
+  return new (Importer.getToContext()) CXXEnumeratingExpansionStmtPattern(
+      ToESD, ToInit, ToExpansionVar, ToLParenLoc, ToColonLoc, ToRParenLoc);
+}
+ExpectedStmt ASTNodeImporter::VisitCXXIteratingExpansionStmtPattern(
+    CXXIteratingExpansionStmtPattern *S) {
+  Error Err = Error::success();
+  auto ToESD = importChecked(Err, S->getDecl());
+  auto ToInit = importChecked(Err, S->getInit());
+  auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt());
+  auto ToRange = importChecked(Err, S->getRangeVarStmt());
+  auto ToBegin = importChecked(Err, S->getBeginVarStmt());
+  auto ToEnd = importChecked(Err, S->getEndVarStmt());
+  auto ToLParenLoc = importChecked(Err, S->getLParenLoc());
+  auto ToColonLoc = importChecked(Err, S->getColonLoc());
+  auto ToRParenLoc = importChecked(Err, S->getRParenLoc());
+  if (Err)
+    return std::move(Err);
+
+  return new (Importer.getToContext()) CXXIteratingExpansionStmtPattern(
+      ToESD, ToInit, ToExpansionVar, ToRange, ToBegin, ToEnd, ToLParenLoc,
+      ToColonLoc, ToRParenLoc);
+}
+ExpectedStmt ASTNodeImporter::VisitCXXDestructuringExpansionStmtPattern(
+    CXXDestructuringExpansionStmtPattern *S) {
+  Error Err = Error::success();
+  auto ToESD = importChecked(Err, S->getDecl());
+  auto ToInit = importChecked(Err, S->getInit());
+  auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt());
+  auto ToDecompositionDeclStmt =
+      importChecked(Err, S->getDecompositionDeclStmt());
+  auto ToLParenLoc = importChecked(Err, S->getLParenLoc());
+  auto ToColonLoc = importChecked(Err, S->getColonLoc());
+  auto ToRParenLoc = importChecked(Err, S->getRParenLoc());
+  if (Err)
+    return std::move(Err);
+
+  return new (Importer.getToContext()) CXXDestructuringExpansionStmtPattern(
+      ToESD, ToInit, ToExpansionVar, ToDecompositionDeclStmt, ToLParenLoc,
+      ToColonLoc, ToRParenLoc);
+}
+ExpectedStmt ASTNodeImporter::VisitCXXDependentExpansionStmtPattern(
+    CXXDependentExpansionStmtPattern *S) {
+  Error Err = Error::success();
+  auto ToESD = importChecked(Err, S->getDecl());
+  auto ToInit = importChecked(Err, S->getInit());
+  auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt());
+  auto ToExpansionInitializer =
+      importChecked(Err, S->getExpansionInitializer());
+  auto ToLParenLoc = importChecked(Err, S->getLParenLoc());
+  auto ToColonLoc = importChecked(Err, S->getColonLoc());
+  auto ToRParenLoc = importChecked(Err, S->getRParenLoc());
+  if (Err)
+    return std::move(Err);
+
+  return new (Importer.getToContext()) CXXDependentExpansionStmtPattern(
+      ToESD, ToInit, ToExpansionVar, ToExpansionInitializer, ToLParenLoc,
+      ToColonLoc, ToRParenLoc);
+}
+ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtInstantiation(
+    CXXExpansionStmtInstantiation *S) {
+  Error Err = Error::success();
+  SmallVector<Stmt *> ToInstantiations;
+  SmallVector<Stmt *> ToSharedStmts;
+  auto ToBeginLoc = importChecked(Err, S->getBeginLoc());
+  auto ToEndLoc = importChecked(Err, S->getEndLoc());
+  for (Stmt *FromInst : S->getInstantiations())
+    ToInstantiations.push_back(importChecked(Err, FromInst));
+  for (Stmt *FromShared : S->getSharedStmts())
+    ToSharedStmts.push_back(importChecked(Err, FromShared));
+
+  if (Err)
+    return std::move(Err);
+
+  return CXXExpansionStmtInstantiation::Create(
+      Importer.getToContext(), ToBeginLoc, ToEndLoc, ToInstantiations,
+      ToSharedStmts, S->shouldApplyLifetimeExtensionToSharedStmts());
+}
+
 ExpectedStmt
 ASTNodeImporter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
   Error Err = Error::success();
@@ -9346,6 +9480,46 @@ ASTNodeImporter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
                                       ToInitLoc, ToBeginLoc, ToEndLoc);
 }
 
+ExpectedStmt
+ASTNodeImporter::VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E) {
+  Error Err = Error::success();
+  SmallVector<Expr *> ToExprs;
+  auto ToLBraceLoc = importChecked(Err, E->getLBraceLoc());
+  auto ToRBraceLoc = importChecked(Err, E->getRBraceLoc());
+  for (Expr *FromInst : E->getExprs())
+    ToExprs.push_back(importChecked(Err, FromInst));
+
+  if (Err)
+    return std::move(Err);
+
+  return CXXExpansionInitListExpr::Create(Importer.getToContext(), ToExprs,
+                                          ToLBraceLoc, ToRBraceLoc);
+}
+
+ExpectedStmt ASTNodeImporter::VisitCXXExpansionInitListSelectExpr(
+    CXXExpansionInitListSelectExpr *E) {
+  Error Err = Error::success();
+  auto ToRange = importChecked(Err, E->getRangeExpr());
+  auto ToIndex = importChecked(Err, E->getIndexExpr());
+  if (Err)
+    return std::move(Err);
+
+  return new (Importer.getToContext())
+      CXXExpansionInitListSelectExpr(Importer.getToContext(), ToRange, ToIndex);
+}
+
+ExpectedStmt ASTNodeImporter::VisitCXXDestructuringExpansionSelectExpr(
+    CXXDestructuringExpansionSelectExpr *E) {
+  Error Err = Error::success();
+  auto ToDecompositionDecl = importChecked(Err, E->getDecompositionDecl());
+  auto ToIndex = importChecked(Err, E->getIndexExpr());
+  if (Err)
+    return std::move(Err);
+
+  return new (Importer.getToContext()) CXXDestructuringExpansionSelectExpr(
+      Importer.getToContext(), ToDecompositionDecl, ToIndex);
+}
+
 Error ASTNodeImporter::ImportOverriddenMethods(CXXMethodDecl *ToMethod,
                                                CXXMethodDecl *FromMethod) {
   Error ImportErrors = Error::success();
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 638080ea781a9..2ff9d74f1a8d5 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -959,3 +959,10 @@ ExprDependence clang::computeDependence(OpenACCAsteriskSizeExpr *E) {
   // way.
   return ExprDependence::None;
 }
+
+ExprDependence clang::computeDependence(CXXExpansionInitListExpr *ILE) {
+  auto D = ExprDependence::None;
+  for (Expr *E : ILE->getExprs())
+    D |= E->getDependence();
+  return D;
+}
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 30c6d3ed91f1e..3cbe309d75528 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -325,6 +325,9 @@ unsigned Decl::getTemplateDepth() const {
   if (auto *TPL = getDescribedTemplateParams())
     return TPL->getDepth() + 1;
 
+  if (auto *ESD = dyn_cast<CXXExpansionStmtDecl>(this))
+    return ESD->getIndexTemplateParm()->getDepth() + 1;
+
   // If this is a dependent lambda, there might be an enclosing variable
   // template. In this case, the next step is not the parent DeclContext (or
   // even a DeclContext at all).
@@ -1018,6 +1021,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
     case ImplicitConceptSpecialization:
     case OpenACCDeclare:
     case OpenACCRoutine:
+    case CXXExpansionStmt:
       // Never looked up by name.
       return 0;
   }
@@ -1382,7 +1386,7 @@ bool DeclContext::isDependentContext() const {
   if (isFileContext())
     return false;
 
-  if (isa<ClassTemplatePartialSpecializationDecl>(this))
+  if (isa<ClassTemplatePartialSpecializationDecl, CXXExpansionStmtDecl>(this))
     return true;
 
   if (const auto *Record = dyn_cast<CXXRecordDecl>(this)) {
@@ -1491,6 +1495,7 @@ DeclContext *DeclContext::getPrimaryContext() {
   case Decl::OMPDeclareReduction:
   case Decl::OMPDeclareMapper:
   case Decl::RequiresExprBody:
+  case Decl::CXXExpansionStmt:
     // There is only one DeclContext for these entities.
     return this;
 
@@ -2079,6 +2084,13 @@ RecordDecl *DeclContext::getOuterLexicalRecordContext() {
   return OutermostRD;
 }
 
+DeclContext *DeclContext::getEnclosingNonExpansionStatementContext() {
+  DeclContext *DC = this;
+  while (isa<CXXExpansionStmtDecl>(DC))
+    DC = DC->getParent();
+  return DC;
+}
+
 bool DeclContext::InEnclosingNamespaceSetOf(const DeclContext *O) const {
   // For non-file contexts, this is equivalent to Equals.
   if (!isFileContext())
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 47ae613b643b6..9e399b3a81ead 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -113,6 +113,7 @@ namespace {
     void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *NTTP);
     void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *);
     void VisitHLSLBufferDecl(HLSLBufferDecl *D);
+    void VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *D);
 
     void VisitOpenACCDeclareDecl(OpenACCDeclareDecl *D);
     void VisitOpenACCRoutineDecl(OpenACCRoutineDecl *D);
@@ -1329,6 +1330,11 @@ void DeclPrinter::VisitClassTemplatePartialSpecializationDecl(
   VisitCXXRecordDecl(D);
 }
 
+void DeclPrinter::VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *D) {
+  D->getExpansionPattern()->printPretty(Out, nullptr, Policy, Indentation, "\n",
+                                        &Context);
+}
+
 //----------------------------------------------------------------------------
 // Objective-C declarations
 //----------------------------------------------------------------------------
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 2f7ae6d6cac63..edfd5cf91196e 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1717,6 +1717,10 @@ clang::getReplacedTemplateParameter(Decl *D, unsigned Index) {
     return getReplacedTemplateParameter(
         cast<FunctionDecl>(D)->getTemplateSpecializationInfo()->getTemplate(),
         Index);
+  case Decl::Kind::CXXExpansionStmt:
+    return {
+        cast<CXXExpansionStmtDecl>(D)->getTemplateParameters()->getParam(Index),
+        {}};
   default:
     llvm_unreachable("Unhandled templated declaration kind");
   }
@@ -1788,3 +1792,22 @@ const Decl &clang::adjustDeclToTemplate(const Decl &D) {
   // FIXME: Adjust alias templates?
   return D;
 }
+
+CXXExpansionStmtDecl::CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
+                                           TemplateParameterList *TParams)
+    : Decl(CXXExpansionStmt, DC, Loc), DeclContext(CXXExpansionStmt),
+      TParams(TParams) {}
+
+CXXExpansionStmtDecl *
+CXXExpansionStmtDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation Loc,
+                             TemplateParameterList *TParams) {
+  return new (C, DC) CXXExpansionStmtDecl(DC, Loc, TParams);
+}
+CXXExpansionStmtDecl *
+CXXExpansionStmtDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
+  return new (C, ID) CXXExpansionStmtDecl(nullptr, SourceLocation(), nullptr);
+}
+
+SourceRange CXXExpansionStmtDecl::getSourceRange() const {
+  return Expansion ? Expansion->getSourceRange() : SourceRange();
+}
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 340bb4b2ed6a3..c61660c90513f 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3688,6 +3688,9 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
   case FunctionParmPackExprClass:
   case RecoveryExprClass:
   case CXXFoldExprClass:
+  case CXXExpansionInitListSelectExprClass:
+  case CXXExpansionInitListExprClass:
+  case CXXDestructuringExpansionSelectExprClass:
     // Make a conservative assumption for dependent nodes.
     return IncludePossibleEffects;
 
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index c7f0ff040194d..7ba49f74c1f7d 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -2020,3 +2020,61 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
   SubExprs[SubExpr::RHS] = RHS;
   setDependence(computeDependence(this));
 }
+
+CXXExpansionInitListExpr::CXXExpansionInitListExpr(EmptyShell ES,
+                                                   unsigned NumExprs)
+    : Expr(CXXExpansionInitListExprClass, ES), NumExprs(NumExprs) {}
+
+CXXExpansionInitListExpr::CXXExpansionInitListExpr(ArrayRef<Expr *> Exprs,
+                                                   SourceLocation LBraceLoc,
+                                                   SourceLocation RBraceLoc)
+    : Expr(CXXExpansionInitListExprClass, QualType(), VK_PRValue, OK_Ordinary),
+      NumExprs(static_cast<unsigned>(Exprs.size())), LBraceLoc(LBraceLoc),
+      RBraceLoc(RBraceLoc) {
+  llvm::uninitialized_copy(Exprs, getTrailingObjects());
+  setDependence(computeDependence(this));
+}
+
+CXXExpansionInitListExpr *
+CXXExpansionInitListExpr::Create(const ASTContext &C, ArrayRef<Expr *> Exprs,
+                                 SourceLocation LBraceLoc,
+                                 SourceLocation RBraceLoc) {
+  void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(Exprs.size()));
+  return new (Mem) CXXExpansionInitListExpr(Exprs, LBraceLoc, RBraceLoc);
+}
+
+CXXExpansionInitListExpr *
+CXXExpansionInitListExpr::CreateEmpty(const ASTContext &C, EmptyShell Empty,
+                                      unsigned NumExprs) {
+  void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(NumExprs));
+  return new (Mem) CXXExpansionInitListExpr(Empty, NumExprs);
+}
+
+CXXExpansionInitListSelectExpr::CXXExpansionInitListSelectExpr(EmptyShell Empty)
+    : Expr(CXXExpansionInitListSelectExprClass, Empty) {}
+
+CXXExpansionInitListSelectExpr::CXXExpansionInitListSelectExpr(
+    const ASTContext &C, CXXExpansionInitListExpr *Range, Expr *Idx)
+    : Expr(CXXExpansionInitListSelectExprClass, C.DependentTy, VK_PRValue,
+           OK_Ordinary) {
+  setDependence(ExprDependence::TypeValueInstantiation);
+  SubExprs[RANGE] = Range;
+  SubExprs[INDEX] = Idx;
+}
+
+bool CXXExpansionInitListExpr::containsPackExpansion() const {
+  return llvm::any_of(getExprs(),
+                      [](const Expr *E) { return isa<PackExpansionExpr>(E); });
+}
+
+CXXDestructuringExpansionSelectExpr::CXXDestructuringExpansionSelectExpr(
+    EmptyShell Empty)
+    : Expr(CXXDestructuringExpansionSelectExprClass, Empty) {}
+
+CXXDestructuringExpansionSelectExpr::CXXDestructuringExpansionSelectExpr(
+    const ASTContext &C, DecompositionDecl *Decomposition, Expr *Index)
+    : Expr(CXXDestructuringExpansionSelectExprClass, C.DependentTy, VK_PRValue,
+           OK_Ordinary),
+      Decomposition(Decomposition), Index(Index) {
+  setDependence(ExprDependence::TypeValueInstantiation);
+}
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index aeacd0dc765ef..5521cdf9d04c9 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -216,6 +216,9 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
   case Expr::SourceLocExprClass:
   case Expr::ConceptSpecializationExprClass:
   case Expr::RequiresExprClass:
+  case Expr::CXXExpansionInitListExprClass:
+  case Expr::CXXExpansionInitListSelectExprClass:
+  case Expr::CXXDestructuringExpansionSelectExprClass:
     return Cl::CL_PRValue;
 
   case Expr::EmbedExprClass:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3b91678f7d400..d93f87a27e68d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20276,6 +20276,9 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
   case Expr::SYCLUniqueStableNameExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::HLSLOutArgExprClass:
+  case Expr::CXXExpansionInitListExprClass:
+  case Expr::CXXExpansionInitListSelectExprClass:
+  case Expr::CXXDestructuringExpansionSelectExprClass:
     return ICEDiag(IK_NotICE, E->getBeginLoc());
 
   case Expr::InitListExprClass: {
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 5572e0a7ae59c..28883edc34e6d 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -42,7 +42,7 @@ using namespace clang;
 namespace {
 
 static bool isLocalContainerContext(const DeclContext *DC) {
-  return isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC) || isa<BlockDecl>(DC);
+  return isa<FunctionDecl, ObjCMethodDecl, BlockDecl, CXXExpansionStmtDecl>(DC);
 }
 
 static const FunctionDecl *getStructor(const FunctionDecl *fn) {
@@ -1851,6 +1851,8 @@ static GlobalDecl getParentOfLocalEntity(const DeclContext *DC) {
     GD = GlobalDecl(CD, Ctor_Complete);
   else if (auto *DD = dyn_cast<CXXDestructorDecl>(DC))
     GD = GlobalDecl(DD, Dtor_Complete);
+  else if (DC->isExpansionStmt())
+    GD = getParentOfLocalEntity(DC->getEnclosingNonExpansionStatementContext());
   else
     GD = GlobalDecl(cast<FunctionDecl>(DC));
   return GD;
@@ -2191,6 +2193,9 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) {
   if (NoFunction && isLocalContainerContext(DC))
     return;
 
+  if (DC->isExpansionStmt())
+    return;
+
   const NamedDecl *ND = cast<NamedDecl>(DC);
   if (mangleSubstitution(ND))
     return;
@@ -4940,6 +4945,9 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   case Expr::CXXInheritedCtorInitExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::PackIndexingExprClass:
+  case Expr::CXXExpansionInitListSelectExprClass:
+  case Expr::CXXExpansionInitListExprClass:
+  case Expr::CXXDestructuringExpansionSelectExprClass:
     llvm_unreachable("unexpected statement kind");
 
   case Expr::ConstantExprClass:
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index 6a69fe75136f3..87bf376f3770d 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/StmtCXX.h"
+#include "clang/AST/ExprCXX.h"
 
 #include "clang/AST/ASTContext.h"
 
@@ -125,3 +126,155 @@ CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args)
       Args.ReturnStmtOnAllocFailure;
   llvm::copy(Args.ParamMoves, const_cast<Stmt **>(getParamMoves().data()));
 }
+
+CXXExpansionStmtPattern::CXXExpansionStmtPattern(StmtClass SC, EmptyShell Empty)
+    : Stmt(SC, Empty) {}
+
+CXXExpansionStmtPattern::CXXExpansionStmtPattern(
+    StmtClass SC, CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
+
+    SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation RParenLoc)
+    : Stmt(SC), ParentDecl(ESD), LParenLoc(LParenLoc), ColonLoc(ColonLoc),
+      RParenLoc(RParenLoc) {
+  setInit(Init);
+  setExpansionVarStmt(ExpansionVar);
+  setBody(nullptr);
+}
+
+CXXEnumeratingExpansionStmtPattern::CXXEnumeratingExpansionStmtPattern(
+    EmptyShell Empty)
+    : CXXExpansionStmtPattern(CXXEnumeratingExpansionStmtPatternClass, Empty) {}
+
+CXXEnumeratingExpansionStmtPattern::CXXEnumeratingExpansionStmtPattern(
+    CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
+    SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation RParenLoc)
+    : CXXExpansionStmtPattern(CXXEnumeratingExpansionStmtPatternClass, ESD,
+                              Init, ExpansionVar, LParenLoc, ColonLoc,
+                              RParenLoc) {}
+
+SourceLocation CXXExpansionStmtPattern::getBeginLoc() const {
+  return ParentDecl->getLocation();
+}
+
+VarDecl *CXXExpansionStmtPattern::getExpansionVariable() {
+  Decl *LV = cast<DeclStmt>(getExpansionVarStmt())->getSingleDecl();
+  assert(LV && "No expansion variable in CXXExpansionStmtPattern");
+  return cast<VarDecl>(LV);
+}
+
+bool CXXExpansionStmtPattern::hasDependentSize() const {
+  if (isa<CXXEnumeratingExpansionStmtPattern>(this))
+    return cast<CXXExpansionInitListSelectExpr>(
+               getExpansionVariable()->getInit())
+        ->getRangeExpr()
+        ->containsPackExpansion();
+
+  if (auto *Iterating = dyn_cast<CXXIteratingExpansionStmtPattern>(this)) {
+    const Expr *Begin = Iterating->getBeginVar()->getInit();
+    const Expr *End = Iterating->getBeginVar()->getInit();
+    return Begin->isTypeDependent() || Begin->isValueDependent() ||
+           End->isTypeDependent() || End->isValueDependent();
+  }
+
+  if (isa<CXXDestructuringExpansionStmtPattern>(this))
+    return false;
+
+  if (isa<CXXDependentExpansionStmtPattern>(this))
+    return true;
+
+  llvm_unreachable("Invalid expansion statement class");
+}
+
+CXXIteratingExpansionStmtPattern::CXXIteratingExpansionStmtPattern(
+    EmptyShell Empty)
+    : CXXExpansionStmtPattern(CXXIteratingExpansionStmtPatternClass, Empty) {}
+
+CXXIteratingExpansionStmtPattern::CXXIteratingExpansionStmtPattern(
+    CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
+    DeclStmt *Range, DeclStmt *Begin, DeclStmt *End, SourceLocation LParenLoc,
+    SourceLocation ColonLoc, SourceLocation RParenLoc)
+    : CXXExpansionStmtPattern(CXXIteratingExpansionStmtPatternClass, ESD, Init,
+                              ExpansionVar, LParenLoc, ColonLoc, RParenLoc) {
+  setRangeVarStmt(Range);
+  setBeginVarStmt(Begin);
+  setEndVarStmt(End);
+}
+
+CXXDestructuringExpansionStmtPattern::CXXDestructuringExpansionStmtPattern(
+    EmptyShell Empty)
+    : CXXExpansionStmtPattern(CXXDestructuringExpansionStmtPatternClass,
+                              Empty) {}
+
+CXXDestructuringExpansionStmtPattern::CXXDestructuringExpansionStmtPattern(
+    CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
+    Stmt *DecompositionDeclStmt, SourceLocation LParenLoc,
+    SourceLocation ColonLoc, SourceLocation RParenLoc)
+    : CXXExpansionStmtPattern(CXXDestructuringExpansionStmtPatternClass, ESD,
+                              Init, ExpansionVar, LParenLoc, ColonLoc,
+                              RParenLoc) {
+  setDecompositionDeclStmt(DecompositionDeclStmt);
+}
+
+DecompositionDecl *
+CXXDestructuringExpansionStmtPattern::getDecompositionDecl() {
+  return cast<DecompositionDecl>(
+      cast<DeclStmt>(getDecompositionDeclStmt())->getSingleDecl());
+}
+
+CXXDependentExpansionStmtPattern::CXXDependentExpansionStmtPattern(
+    EmptyShell Empty)
+    : CXXExpansionStmtPattern(CXXDependentExpansionStmtPatternClass, Empty) {}
+
+CXXDependentExpansionStmtPattern::CXXDependentExpansionStmtPattern(
+    CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
+    Expr *ExpansionInitializer, SourceLocation LParenLoc,
+    SourceLocation ColonLoc, SourceLocation RParenLoc)
+    : CXXExpansionStmtPattern(CXXDependentExpansionStmtPatternClass, ESD, Init,
+                              ExpansionVar, LParenLoc, ColonLoc, RParenLoc) {
+  setExpansionInitializer(ExpansionInitializer);
+}
+
+CXXExpansionStmtInstantiation::CXXExpansionStmtInstantiation(
+    EmptyShell Empty, unsigned NumInstantiations, unsigned NumSharedStmts)
+    : Stmt(CXXExpansionStmtInstantiationClass, Empty),
+      NumInstantiations(NumInstantiations), NumSharedStmts(NumSharedStmts) {
+  assert(NumSharedStmts <= 4 && "might have to allocate more bits for this");
+}
+
+CXXExpansionStmtInstantiation::CXXExpansionStmtInstantiation(
+    SourceLocation BeginLoc, SourceLocation EndLoc,
+    ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> SharedStmts,
+    bool ShouldApplyLifetimeExtensionToSharedStmts)
+    : Stmt(CXXExpansionStmtInstantiationClass), BeginLoc(BeginLoc),
+      EndLoc(EndLoc), NumInstantiations(unsigned(Instantiations.size())),
+      NumSharedStmts(unsigned(SharedStmts.size())),
+      ShouldApplyLifetimeExtensionToSharedStmts(
+          ShouldApplyLifetimeExtensionToSharedStmts) {
+  assert(NumSharedStmts <= 4 && "might have to allocate more bits for this");
+  llvm::uninitialized_copy(Instantiations, getTrailingObjects());
+  llvm::uninitialized_copy(SharedStmts,
+                           getTrailingObjects() + NumInstantiations);
+}
+
+CXXExpansionStmtInstantiation *CXXExpansionStmtInstantiation::Create(
+    ASTContext &C, SourceLocation BeginLoc, SourceLocation EndLoc,
+    ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> SharedStmts,
+    bool ShouldApplyLifetimeExtensionToSharedStmts) {
+  void *Mem = C.Allocate(
+      totalSizeToAlloc<Stmt *>(Instantiations.size() + SharedStmts.size()),
+      alignof(CXXExpansionStmtInstantiation));
+  return new (Mem) CXXExpansionStmtInstantiation(
+      BeginLoc, EndLoc, Instantiations, SharedStmts,
+      ShouldApplyLifetimeExtensionToSharedStmts);
+}
+
+CXXExpansionStmtInstantiation *
+CXXExpansionStmtInstantiation::CreateEmpty(ASTContext &C, EmptyShell Empty,
+                                           unsigned NumInstantiations,
+                                           unsigned NumSharedStmts) {
+  void *Mem =
+      C.Allocate(totalSizeToAlloc<Stmt *>(NumInstantiations + NumSharedStmts),
+                 alignof(CXXExpansionStmtInstantiation));
+  return new (Mem)
+      CXXExpansionStmtInstantiation(Empty, NumInstantiations, NumSharedStmts);
+}
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index ff8ca01ec5477..6760d3bbe54cc 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -160,6 +160,8 @@ namespace {
     }
 
     void VisitCXXNamedCastExpr(CXXNamedCastExpr *Node);
+    void VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node,
+                                      Expr *Initializer = nullptr);
 
 #define ABSTRACT_STMT(CLASS)
 #define STMT(CLASS, PARENT) \
@@ -263,7 +265,8 @@ void StmtPrinter::VisitDeclStmt(DeclStmt *Node) {
   PrintRawDeclStmt(Node);
   // Certain pragma declarations shouldn't have a semi-colon after them.
   if (!Node->isSingleDecl() ||
-      !isa<OpenACCDeclareDecl, OpenACCRoutineDecl>(Node->getSingleDecl()))
+      !isa<CXXExpansionStmtDecl, OpenACCDeclareDecl, OpenACCRoutineDecl>(
+          Node->getSingleDecl()))
     OS << ";";
   OS << NL;
 }
@@ -447,6 +450,63 @@ void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt *Node) {
   PrintControlledStmt(Node->getBody());
 }
 
+void StmtPrinter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node,
+                                               Expr *Initializer) {
+  OS << "template for (";
+  if (Node->getInit())
+    PrintInitStmt(Node->getInit(), 14);
+  PrintingPolicy SubPolicy(Policy);
+  SubPolicy.SuppressInitializers = true;
+  Node->getExpansionVariable()->print(OS, SubPolicy, IndentLevel);
+  OS << " : ";
+  PrintExpr(Initializer ? Initializer
+                        : Node->getExpansionVariable()->getInit());
+  OS << ")";
+  PrintControlledStmt(Node->getBody());
+}
+
+void StmtPrinter::VisitCXXEnumeratingExpansionStmtPattern(
+    CXXEnumeratingExpansionStmtPattern *Node) {
+  VisitCXXExpansionStmtPattern(Node);
+}
+
+void StmtPrinter::VisitCXXIteratingExpansionStmtPattern(
+    CXXIteratingExpansionStmtPattern *Node) {
+  VisitCXXExpansionStmtPattern(Node, Node->getRangeVar()->getInit());
+}
+
+void StmtPrinter::VisitCXXDestructuringExpansionStmtPattern(
+    CXXDestructuringExpansionStmtPattern *Node) {
+  VisitCXXExpansionStmtPattern(Node);
+}
+
+void StmtPrinter::VisitCXXDependentExpansionStmtPattern(
+    CXXDependentExpansionStmtPattern *Node) {
+  VisitCXXExpansionStmtPattern(Node, Node->getExpansionInitializer());
+}
+
+void StmtPrinter::VisitCXXExpansionStmtInstantiation(
+    CXXExpansionStmtInstantiation *) {
+  llvm_unreachable("should never be printed");
+}
+
+void StmtPrinter::VisitCXXExpansionInitListExpr(
+    CXXExpansionInitListExpr *Node) {
+  OS << "{ ";
+  llvm::interleaveComma(Node->getExprs(), OS, [&](Expr *E) { PrintExpr(E); });
+  OS << " }";
+}
+
+void StmtPrinter::VisitCXXExpansionInitListSelectExpr(
+    CXXExpansionInitListSelectExpr *Node) {
+  PrintExpr(Node->getRangeExpr());
+}
+
+void StmtPrinter::VisitCXXDestructuringExpansionSelectExpr(
+    CXXDestructuringExpansionSelectExpr *Node) {
+  PrintExpr(Node->getDecompositionDecl()->getInit());
+}
+
 void StmtPrinter::VisitMSDependentExistsStmt(MSDependentExistsStmt *Node) {
   Indent();
   if (Node->isIfExists())
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 4a8c638c85331..45513c479e1d1 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -364,6 +364,37 @@ void StmtProfiler::VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
   VisitStmt(S);
 }
 
+void StmtProfiler::VisitCXXExpansionStmtPattern(
+    const CXXExpansionStmtPattern *S) {
+  VisitStmt(S);
+}
+
+void StmtProfiler::VisitCXXEnumeratingExpansionStmtPattern(
+    const CXXEnumeratingExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+}
+
+void StmtProfiler::VisitCXXIteratingExpansionStmtPattern(
+    const CXXIteratingExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+}
+
+void StmtProfiler::VisitCXXDestructuringExpansionStmtPattern(
+    const CXXDestructuringExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+}
+
+void StmtProfiler::VisitCXXDependentExpansionStmtPattern(
+    const CXXDependentExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+}
+
+void StmtProfiler::VisitCXXExpansionStmtInstantiation(
+    const CXXExpansionStmtInstantiation *S) {
+  VisitStmt(S);
+  ID.AddBoolean(S->shouldApplyLifetimeExtensionToSharedStmts());
+}
+
 void StmtProfiler::VisitMSDependentExistsStmt(const MSDependentExistsStmt *S) {
   VisitStmt(S);
   ID.AddBoolean(S->isIfExists());
@@ -2400,6 +2431,22 @@ void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) {
 
 void StmtProfiler::VisitEmbedExpr(const EmbedExpr *E) { VisitExpr(E); }
 
+void StmtProfiler::VisitCXXExpansionInitListExpr(
+    const CXXExpansionInitListExpr *E) {
+  VisitExpr(E);
+}
+
+void StmtProfiler::VisitCXXExpansionInitListSelectExpr(
+    const CXXExpansionInitListSelectExpr *E) {
+  VisitExpr(E);
+}
+
+void StmtProfiler::VisitCXXDestructuringExpansionSelectExpr(
+    const CXXDestructuringExpansionSelectExpr *E) {
+  VisitExpr(E);
+  VisitDecl(E->getDecompositionDecl());
+}
+
 void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); }
 
 void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 41aebdb8d2f1b..4d19bf63f3536 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -951,7 +951,11 @@ void TextNodeDumper::dumpBareDeclRef(const Decl *D) {
       switch (ND->getKind()) {
       case Decl::Decomposition: {
         auto *DD = cast<DecompositionDecl>(ND);
-        OS << " first_binding '" << DD->bindings()[0]->getDeclName() << '\'';
+
+        // Empty decomposition decls can occur in destructuring expansion
+        // statements.
+        if (!DD->bindings().empty())
+          OS << " first_binding '" << DD->bindings()[0]->getDeclName() << '\'';
         break;
       }
       case Decl::Field: {
@@ -1495,6 +1499,12 @@ void clang::TextNodeDumper::VisitCoreturnStmt(const CoreturnStmt *Node) {
     OS << " implicit";
 }
 
+void TextNodeDumper::VisitCXXExpansionStmtInstantiation(
+    const CXXExpansionStmtInstantiation *Node) {
+  if (Node->shouldApplyLifetimeExtensionToSharedStmts())
+    OS << " applies_lifetime_extension";
+}
+
 void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) {
   if (Node->hasAPValueResult())
     AddChild("value",
@@ -1826,6 +1836,17 @@ void TextNodeDumper::VisitCXXDependentScopeMemberExpr(
   OS << " " << (Node->isArrow() ? "->" : ".") << Node->getMember();
 }
 
+void TextNodeDumper::VisitCXXExpansionInitListExpr(
+    const CXXExpansionInitListExpr *Node) {
+  if (Node->containsPackExpansion())
+    OS << " contains_pack";
+}
+
+void TextNodeDumper::VisitCXXDestructuringExpansionSelectExpr(
+    const CXXDestructuringExpansionSelectExpr *Node) {
+  dumpDeclRef(Node->getDecompositionDecl());
+}
+
 void TextNodeDumper::VisitObjCMessageExpr(const ObjCMessageExpr *Node) {
   OS << " selector=";
   Node->getSelector().print(OS);
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 46addea232b03..5fbfb4023ee4d 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1632,14 +1632,15 @@ DeclContext *Sema::getFunctionLevelDeclContext(bool AllowLambda) const {
   DeclContext *DC = CurContext;
 
   while (true) {
-    if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC) ||
-        isa<RequiresExprBodyDecl>(DC)) {
+    if (isa<BlockDecl, EnumDecl, CapturedDecl, RequiresExprBodyDecl,
+            CXXExpansionStmtDecl>(DC)) {
       DC = DC->getParent();
     } else if (!AllowLambda && isa<CXXMethodDecl>(DC) &&
                cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call &&
                cast<CXXRecordDecl>(DC->getParent())->isLambda()) {
       DC = DC->getParent()->getParent();
-    } else break;
+    } else
+      break;
   }
 
   return DC;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 651437a6f4c30..468376039fae5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7437,7 +7437,7 @@ static bool shouldConsiderLinkage(const VarDecl *VD) {
   if (DC->getDeclKind() == Decl::HLSLBuffer)
     return false;
 
-  if (isa<RequiresExprBodyDecl>(DC))
+  if (isa<RequiresExprBodyDecl, CXXExpansionStmtDecl>(DC))
     return false;
   llvm_unreachable("Unexpected context");
 }
@@ -7447,7 +7447,7 @@ static bool shouldConsiderLinkage(const FunctionDecl *FD) {
   if (DC->isFileContext() || DC->isFunctionOrMethod() ||
       isa<OMPDeclareReductionDecl>(DC) || isa<OMPDeclareMapperDecl>(DC))
     return true;
-  if (DC->isRecord())
+  if (DC->isRecord() || isa<CXXExpansionStmtDecl>(DC))
     return false;
   llvm_unreachable("Unexpected context");
 }
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index a0483c3027199..a56f933638190 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1288,6 +1288,9 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::ConvertVectorExprClass:
   case Expr::VAArgExprClass:
   case Expr::CXXParenListInitExprClass:
+  case Expr::CXXExpansionInitListSelectExprClass:
+  case Expr::CXXExpansionInitListExprClass:
+  case Expr::CXXDestructuringExpansionSelectExprClass:
     return canSubStmtsThrow(*this, S);
 
   case Expr::CompoundLiteralExprClass:
@@ -1348,6 +1351,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::DependentScopeDeclRefExprClass:
   case Expr::CXXFoldExprClass:
   case Expr::RecoveryExprClass:
+  case Expr::CXXDependentExpansionStmtPatternClass:
     return CT_Dependent;
 
   case Expr::AsTypeExprClass:
@@ -1538,6 +1542,10 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Stmt::SEHTryStmtClass:
   case Stmt::SwitchStmtClass:
   case Stmt::WhileStmtClass:
+  case Stmt::CXXEnumeratingExpansionStmtPatternClass:
+  case Stmt::CXXIteratingExpansionStmtPatternClass:
+  case Stmt::CXXDestructuringExpansionStmtPatternClass:
+  case Stmt::CXXExpansionStmtInstantiationClass:
     return canSubStmtsThrow(*this, S);
 
   case Stmt::DeclStmtClass: {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d3c2cc559ea20..7fc6d3cff36b0 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19300,11 +19300,12 @@ bool Sema::tryCaptureVariable(
     QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) {
   // An init-capture is notionally from the context surrounding its
   // declaration, but its parent DC is the lambda class.
-  DeclContext *VarDC = Var->getDeclContext();
+  DeclContext *VarDC =
+      Var->getDeclContext()->getEnclosingNonExpansionStatementContext();
   DeclContext *DC = CurContext;
 
   // Skip past RequiresExprBodys because they don't constitute function scopes.
-  while (DC->isRequiresExprBody())
+  while (DC->isRequiresExprBody() || DC->isExpansionStmt())
     DC = DC->getParent();
 
   // tryCaptureVariable is called every time a DeclRef is formed,
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 43bcb4f743cfa..0d7e2a9c5b324 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7600,7 +7600,6 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(
     Expr *const FE, LambdaScopeInfo *const CurrentLSI, Sema &S) {
 
   assert(!S.isUnevaluatedContext());
-  assert(S.CurContext->isDependentContext());
 #ifndef NDEBUG
   DeclContext *DC = S.CurContext;
   while (isa_and_nonnull<CapturedDecl>(DC))
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index f65c55a209622..c618f4e0a70db 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -100,8 +100,9 @@ static inline UnsignedOrNone getStackIndexOfNearestEnclosingCaptureReadyLambda(
     // innermost nested lambda are dependent (otherwise we wouldn't have
     // arrived here) - so we don't yet have a lambda that can capture the
     // variable.
-    if (IsCapturingVariable &&
-        VarToCapture->getDeclContext()->Equals(EnclosingDC))
+    if (IsCapturingVariable && VarToCapture->getDeclContext()
+                                   ->getEnclosingNonExpansionStatementContext()
+                                   ->Equals(EnclosingDC))
       return NoLambdaIsCaptureReady;
 
     // For an enclosing lambda to be capture ready for an entity, all
@@ -126,7 +127,8 @@ static inline UnsignedOrNone getStackIndexOfNearestEnclosingCaptureReadyLambda(
       if (IsCapturingThis && !LSI->isCXXThisCaptured())
         return NoLambdaIsCaptureReady;
     }
-    EnclosingDC = getLambdaAwareParentOfDeclContext(EnclosingDC);
+    EnclosingDC = getLambdaAwareParentOfDeclContext(EnclosingDC)
+                      ->getEnclosingNonExpansionStatementContext();
 
     assert(CurScopeIndex);
     --CurScopeIndex;
@@ -190,11 +192,6 @@ UnsignedOrNone clang::getStackIndexOfNearestEnclosingCaptureCapableLambda(
     return NoLambdaIsCaptureCapable;
 
   const unsigned IndexOfCaptureReadyLambda = *OptionalStackIndex;
-  assert(((IndexOfCaptureReadyLambda != (FunctionScopes.size() - 1)) ||
-          S.getCurGenericLambda()) &&
-         "The capture ready lambda for a potential capture can only be the "
-         "current lambda if it is a generic lambda");
-
   const sema::LambdaScopeInfo *const CaptureReadyLambdaLSI =
       cast<sema::LambdaScopeInfo>(FunctionScopes[IndexOfCaptureReadyLambda]);
 
@@ -248,7 +245,7 @@ CXXRecordDecl *
 Sema::createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info,
                               unsigned LambdaDependencyKind,
                               LambdaCaptureDefault CaptureDefault) {
-  DeclContext *DC = CurContext;
+  DeclContext *DC = CurContext->getEnclosingNonExpansionStatementContext();
 
   bool IsGenericLambda =
       Info && getGenericLambdaTemplateParameterList(getCurLambda(), *this);
@@ -1376,7 +1373,9 @@ void Sema::ActOnLambdaClosureQualifiers(LambdaIntroducer &Intro,
   // odr-use 'this' (in particular, in a default initializer for a non-static
   // data member).
   if (Intro.Default != LCD_None &&
-      !LSI->Lambda->getParent()->isFunctionOrMethod() &&
+      !LSI->Lambda->getParent()
+           ->getEnclosingNonExpansionStatementContext()
+           ->isFunctionOrMethod() &&
       (getCurrentThisType().isNull() ||
        CheckCXXThisCapture(SourceLocation(), /*Explicit=*/true,
                            /*BuildAndDiagnose=*/false)))
@@ -2519,9 +2518,12 @@ Sema::LambdaScopeForCallOperatorInstantiationRAII::
   while (FDPattern && FD) {
     InstantiationAndPatterns.emplace_back(FDPattern, FD);
 
-    FDPattern =
-        dyn_cast<FunctionDecl>(getLambdaAwareParentOfDeclContext(FDPattern));
-    FD = dyn_cast<FunctionDecl>(getLambdaAwareParentOfDeclContext(FD));
+    FDPattern = dyn_cast<FunctionDecl>(
+        getLambdaAwareParentOfDeclContext(FDPattern)
+            ->getEnclosingNonExpansionStatementContext());
+    FD = dyn_cast<FunctionDecl>(
+        getLambdaAwareParentOfDeclContext(FD)
+            ->getEnclosingNonExpansionStatementContext());
   }
 
   // Add instantiated parameters and local vars to scopes, starting from the
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 5915d6e57d893..88dcd27d45ad2 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -4455,7 +4455,9 @@ LabelDecl *Sema::LookupExistingLabel(IdentifierInfo *II, SourceLocation Loc) {
                                     RedeclarationKind::NotForRedeclaration);
   // If we found a label, check to see if it is in the same context as us.
   // When in a Block, we don't want to reuse a label in an enclosing function.
-  if (!Res || Res->getDeclContext() != CurContext)
+  if (!Res ||
+      Res->getDeclContext()->getEnclosingNonExpansionStatementContext() !=
+          CurContext->getEnclosingNonExpansionStatementContext())
     return nullptr;
   return cast<LabelDecl>(Res);
 }
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 26693514bb278..f8136c3c24a52 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2087,6 +2087,11 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
       InstantiatedMessageExpr.get(), D->getRParenLoc(), D->isFailed());
 }
 
+Decl *TemplateDeclInstantiator::VisitCXXExpansionStmtDecl(
+    CXXExpansionStmtDecl *OldESD) {
+  llvm_unreachable("TODO");
+}
+
 Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
   EnumDecl *PrevDecl = nullptr;
   if (EnumDecl *PatternPrev = getPreviousDeclForInstantiation(D)) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 0e8b674a006d0..f181d0abb5dfd 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9292,6 +9292,59 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
   return FinishCXXForRangeStmt(NewStmt.get(), Body.get());
 }
 
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformCXXEnumeratingExpansionStmtPattern(
+    CXXEnumeratingExpansionStmtPattern *S) {
+  llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformCXXIteratingExpansionStmtPattern(
+    CXXIteratingExpansionStmtPattern *S) {
+  llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformCXXDependentExpansionStmtPattern(
+    CXXDependentExpansionStmtPattern *S) {
+  llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+StmtResult
+TreeTransform<Derived>::TransformCXXDestructuringExpansionStmtPattern(
+    CXXDestructuringExpansionStmtPattern *) {
+  // The only time we instantiate an expansion statement is if its expansion
+  // size is dependent (otherwise, we only instantiate the expansions and
+  // leave the underlying CXXExpansionStmtPattern as-is). Since destructuring
+  // expansion statements never have a dependent size, we should never get here.
+  llvm_unreachable("Should never be instantiated");
+}
+
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCXXExpansionInitListExpr(
+    CXXExpansionInitListExpr *E) {
+  llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
+    CXXExpansionStmtInstantiation *S) {
+  llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCXXExpansionInitListSelectExpr(
+    CXXExpansionInitListSelectExpr *E) {
+  llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCXXDestructuringExpansionSelectExpr(
+    CXXDestructuringExpansionSelectExpr *E) {
+  llvm_unreachable("TOOD");
+}
+
 template<typename Derived>
 StmtResult
 TreeTransform<Derived>::TransformMSDependentExistsStmt(
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index 69db02f2efc40..d07661a5b2f64 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -459,6 +459,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
   case Decl::HLSLRootSignature:
   case Decl::OpenACCDeclare:
   case Decl::OpenACCRoutine:
+  case Decl::CXXExpansionStmt:
     return false;
 
   // These indirectly derive from Redeclarable<T> but are not actually
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 5456e73956659..3086d7315a862 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -405,6 +405,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
   void VisitFriendDecl(FriendDecl *D);
   void VisitFriendTemplateDecl(FriendTemplateDecl *D);
   void VisitStaticAssertDecl(StaticAssertDecl *D);
+  void VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D);
   void VisitBlockDecl(BlockDecl *BD);
   void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D);
   void VisitCapturedDecl(CapturedDecl *CD);
@@ -2769,6 +2770,14 @@ void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) {
   D->RParenLoc = readSourceLocation();
 }
 
+void ASTDeclReader::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
+  VisitDecl(D);
+  D->Expansion = cast<CXXExpansionStmtPattern>(Record.readStmt());
+  D->Instantiations =
+      cast_or_null<CXXExpansionStmtInstantiation>(Record.readStmt());
+  D->TParams = Record.readTemplateParameterList();
+}
+
 void ASTDeclReader::VisitEmptyDecl(EmptyDecl *D) {
   VisitDecl(D);
 }
@@ -4083,6 +4092,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
   case DECL_STATIC_ASSERT:
     D = StaticAssertDecl::CreateDeserialized(Context, ID);
     break;
+  case DECL_EXPANSION_STMT:
+    D = CXXExpansionStmtDecl::CreateDeserialized(Context, ID);
+    break;
   case DECL_OBJC_METHOD:
     D = ObjCMethodDecl::CreateDeserialized(Context, ID);
     break;
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index eef97a8588f0b..c74113c4ab0da 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1730,6 +1730,77 @@ void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
   S->setBody(Record.readSubStmt());
 }
 
+void ASTStmtReader::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
+  VisitStmt(S);
+  S->LParenLoc = readSourceLocation();
+  S->ColonLoc = readSourceLocation();
+  S->RParenLoc = readSourceLocation();
+  S->ParentDecl = cast<CXXExpansionStmtDecl>(Record.readDeclRef());
+  S->setInit(Record.readSubStmt());
+  S->setExpansionVarStmt(Record.readSubStmt());
+  S->setBody(Record.readSubStmt());
+}
+
+void ASTStmtReader::VisitCXXExpansionStmtInstantiation(
+    CXXExpansionStmtInstantiation *S) {
+  VisitStmt(S);
+  Record.skipInts(2);
+  S->BeginLoc = readSourceLocation();
+  S->EndLoc = readSourceLocation();
+  for (unsigned I = 0; I < S->getNumSubStmts(); ++I)
+    S->getAllSubStmts()[I] = Record.readSubStmt();
+  S->setShouldApplyLifetimeExtensionToSharedStmts(Record.readBool());
+}
+
+void ASTStmtReader::VisitCXXEnumeratingExpansionStmtPattern(
+    CXXEnumeratingExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+}
+
+void ASTStmtReader::VisitCXXIteratingExpansionStmtPattern(
+    CXXIteratingExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+  S->setRangeVarStmt(cast<DeclStmt>(Record.readSubStmt()));
+  S->setBeginVarStmt(cast<DeclStmt>(Record.readSubStmt()));
+  S->setEndVarStmt(cast<DeclStmt>(Record.readSubStmt()));
+}
+
+void ASTStmtReader::VisitCXXDestructuringExpansionStmtPattern(
+    CXXDestructuringExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+  S->setDecompositionDeclStmt(Record.readSubStmt());
+}
+
+void ASTStmtReader::VisitCXXDependentExpansionStmtPattern(
+    CXXDependentExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+  S->setExpansionInitializer(Record.readSubExpr());
+}
+
+void ASTStmtReader::VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E) {
+  VisitExpr(E);
+  assert(Record.peekInt() == E->getNumExprs() && "NumExprFields is wrong ?");
+  Record.skipInts(1);
+  E->LBraceLoc = readSourceLocation();
+  E->RBraceLoc = readSourceLocation();
+  for (unsigned I = 0; I < E->getNumExprs(); ++I)
+    E->getExprs()[I] = Record.readSubExpr();
+}
+
+void ASTStmtReader::VisitCXXExpansionInitListSelectExpr(
+    CXXExpansionInitListSelectExpr *E) {
+  VisitExpr(E);
+  E->setRangeExpr(cast<CXXExpansionInitListExpr>(Record.readSubExpr()));
+  E->setIndexExpr(Record.readSubExpr());
+}
+
+void ASTStmtReader::VisitCXXDestructuringExpansionSelectExpr(
+    CXXDestructuringExpansionSelectExpr *E) {
+  VisitExpr(E);
+  E->setDecompositionDecl(cast<DecompositionDecl>(Record.readDeclRef()));
+  E->setIndexExpr(Record.readSubExpr());
+}
+
 void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
   VisitStmt(S);
   S->KeywordLoc = readSourceLocation();
@@ -3566,6 +3637,28 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
              /*numHandlers=*/Record[ASTStmtReader::NumStmtFields]);
       break;
 
+    case STMT_CXX_ENUMERATING_EXPANSION:
+      S = new (Context) CXXEnumeratingExpansionStmtPattern(Empty);
+      break;
+
+    case STMT_CXX_ITERATING_EXPANSION:
+      S = new (Context) CXXIteratingExpansionStmtPattern(Empty);
+      break;
+
+    case STMT_CXX_DESTRUCTURING_EXPANSION:
+      S = new (Context) CXXDestructuringExpansionStmtPattern(Empty);
+      break;
+
+    case STMT_CXX_DEPENDENT_EXPANSION:
+      S = new (Context) CXXDependentExpansionStmtPattern(Empty);
+      break;
+
+    case STMT_CXX_EXPANSION_INSTANTIATION:
+      S = CXXExpansionStmtInstantiation::CreateEmpty(
+          Context, Empty, Record[ASTStmtReader::NumStmtFields],
+          Record[ASTStmtReader::NumStmtFields + 1]);
+      break;
+
     case STMT_CXX_FOR_RANGE:
       S = new (Context) CXXForRangeStmt(Empty);
       break;
@@ -4443,6 +4536,20 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       S = new (Context) ConceptSpecializationExpr(Empty);
       break;
     }
+
+    case EXPR_CXX_EXPANSION_INIT_LIST:
+      S = CXXExpansionInitListExpr::CreateEmpty(
+          Context, Empty, Record[ASTStmtReader::NumExprFields]);
+      break;
+
+    case EXPR_CXX_EXPANSION_INIT_LIST_SELECT:
+      S = new (Context) CXXExpansionInitListSelectExpr(Empty);
+      break;
+
+    case EXPR_CXX_DESTRUCTURING_EXPANSION_SELECT:
+      S = new (Context) CXXDestructuringExpansionSelectExpr(Empty);
+      break;
+
     case STMT_OPENACC_COMPUTE_CONSTRUCT: {
       unsigned NumClauses = Record[ASTStmtReader::NumStmtFields];
       S = OpenACCComputeConstruct::CreateEmpty(Context, NumClauses);
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index c9f8797ab973f..23d11341d131a 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -144,6 +144,7 @@ namespace clang {
     void VisitFriendDecl(FriendDecl *D);
     void VisitFriendTemplateDecl(FriendTemplateDecl *D);
     void VisitStaticAssertDecl(StaticAssertDecl *D);
+    void VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D);
     void VisitBlockDecl(BlockDecl *D);
     void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D);
     void VisitCapturedDecl(CapturedDecl *D);
@@ -2188,6 +2189,14 @@ void ASTDeclWriter::VisitStaticAssertDecl(StaticAssertDecl *D) {
   Code = serialization::DECL_STATIC_ASSERT;
 }
 
+void ASTDeclWriter::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
+  VisitDecl(D);
+  Record.AddStmt(D->getExpansionPattern());
+  Record.AddStmt(D->getInstantiations());
+  Record.AddTemplateParameterList(D->getTemplateParameters());
+  Code = serialization::DECL_EXPANSION_STMT;
+}
+
 /// Emit the DeclContext part of a declaration context decl.
 void ASTDeclWriter::VisitDeclContext(DeclContext *DC) {
   static_assert(DeclContext::NumDeclContextBits == 13,
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index acf345392aa1a..d795b23525888 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1704,6 +1704,85 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
   Code = serialization::STMT_CXX_FOR_RANGE;
 }
 
+void ASTStmtWriter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
+  VisitStmt(S);
+  Record.AddSourceLocation(S->getLParenLoc());
+  Record.AddSourceLocation(S->getColonLoc());
+  Record.AddSourceLocation(S->getRParenLoc());
+  Record.AddDeclRef(S->getDecl());
+  Record.AddStmt(S->getInit());
+  Record.AddStmt(S->getExpansionVarStmt());
+  Record.AddStmt(S->getBody());
+}
+
+void ASTStmtWriter::VisitCXXExpansionStmtInstantiation(
+    CXXExpansionStmtInstantiation *S) {
+  VisitStmt(S);
+  Record.push_back(S->getInstantiations().size());
+  Record.push_back(S->getSharedStmts().size());
+  Record.AddSourceLocation(S->getBeginLoc());
+  Record.AddSourceLocation(S->getEndLoc());
+  for (Stmt *St : S->getAllSubStmts())
+    Record.AddStmt(St);
+  Record.push_back(S->shouldApplyLifetimeExtensionToSharedStmts());
+  Code = serialization::STMT_CXX_EXPANSION_INSTANTIATION;
+}
+
+void ASTStmtWriter::VisitCXXEnumeratingExpansionStmtPattern(
+    CXXEnumeratingExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+  Code = serialization::STMT_CXX_ENUMERATING_EXPANSION;
+}
+
+void ASTStmtWriter::VisitCXXIteratingExpansionStmtPattern(
+    CXXIteratingExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+  Record.AddStmt(S->getRangeVarStmt());
+  Record.AddStmt(S->getBeginVarStmt());
+  Record.AddStmt(S->getEndVarStmt());
+  Code = serialization::STMT_CXX_ITERATING_EXPANSION;
+}
+
+void ASTStmtWriter::VisitCXXDestructuringExpansionStmtPattern(
+    CXXDestructuringExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+  Record.AddStmt(S->getDecompositionDeclStmt());
+  Code = serialization::STMT_CXX_DESTRUCTURING_EXPANSION;
+}
+
+void ASTStmtWriter::VisitCXXDependentExpansionStmtPattern(
+    CXXDependentExpansionStmtPattern *S) {
+  VisitCXXExpansionStmtPattern(S);
+  Record.AddStmt(S->getExpansionInitializer());
+  Code = serialization::STMT_CXX_DEPENDENT_EXPANSION;
+}
+
+void ASTStmtWriter::VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E) {
+  VisitExpr(E);
+  Record.push_back(E->getNumExprs());
+  Record.AddSourceLocation(E->getLBraceLoc());
+  Record.AddSourceLocation(E->getRBraceLoc());
+  for (Expr *SubExpr : E->getExprs())
+    Record.AddStmt(SubExpr);
+  Code = serialization::EXPR_CXX_EXPANSION_INIT_LIST;
+}
+
+void ASTStmtWriter::VisitCXXExpansionInitListSelectExpr(
+    CXXExpansionInitListSelectExpr *E) {
+  VisitExpr(E);
+  Record.AddStmt(E->getRangeExpr());
+  Record.AddStmt(E->getIndexExpr());
+  Code = serialization::EXPR_CXX_EXPANSION_INIT_LIST_SELECT;
+}
+
+void ASTStmtWriter::VisitCXXDestructuringExpansionSelectExpr(
+    CXXDestructuringExpansionSelectExpr *E) {
+  VisitExpr(E);
+  Record.AddDeclRef(E->getDecompositionDecl());
+  Record.AddStmt(E->getIndexExpr());
+  Code = serialization::EXPR_CXX_DESTRUCTURING_EXPANSION_SELECT;
+}
+
 void ASTStmtWriter::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
   VisitStmt(S);
   Record.AddSourceLocation(S->getKeywordLoc());
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index a759aee47b8ea..38106f154c15e 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1752,6 +1752,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::SEHExceptStmtClass:
     case Stmt::SEHLeaveStmtClass:
     case Stmt::SEHFinallyStmtClass:
+    case Stmt::CXXEnumeratingExpansionStmtPatternClass:
+    case Stmt::CXXIteratingExpansionStmtPatternClass:
+    case Stmt::CXXDestructuringExpansionStmtPatternClass:
+    case Stmt::CXXDependentExpansionStmtPatternClass:
+    case Stmt::CXXExpansionStmtInstantiationClass:
+    case Stmt::CXXExpansionInitListExprClass:
+    case Stmt::CXXExpansionInitListSelectExprClass:
+    case Stmt::CXXDestructuringExpansionSelectExprClass:
     case Stmt::OMPCanonicalLoopClass:
     case Stmt::OMPParallelDirectiveClass:
     case Stmt::OMPSimdDirectiveClass:
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index f4d6fa72a1dfe..d4da1cc8ba722 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -7254,6 +7254,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
   case Decl::UnresolvedUsingIfExists:
   case Decl::OpenACCDeclare:
   case Decl::OpenACCRoutine:
+  case Decl::CXXExpansionStmt:
     return C;
 
   // Declaration kinds that don't make any sense here, but are
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index 0a43d73063c1f..f0ae4f0b593a2 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -290,6 +290,11 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
 
   case Stmt::CoroutineBodyStmtClass:
   case Stmt::CoreturnStmtClass:
+  case Stmt::CXXEnumeratingExpansionStmtPatternClass:
+  case Stmt::CXXIteratingExpansionStmtPatternClass:
+  case Stmt::CXXDestructuringExpansionStmtPatternClass:
+  case Stmt::CXXDependentExpansionStmtPatternClass:
+  case Stmt::CXXExpansionStmtInstantiationClass:
     K = CXCursor_UnexposedStmt;
     break;
 
@@ -338,6 +343,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
   case Stmt::EmbedExprClass:
   case Stmt::HLSLOutArgExprClass:
   case Stmt::OpenACCAsteriskSizeExprClass:
+  case Stmt::CXXExpansionInitListExprClass:
+  case Stmt::CXXExpansionInitListSelectExprClass:
+  case Stmt::CXXDestructuringExpansionSelectExprClass:
     K = CXCursor_UnexposedExpr;
     break;
 

>From 902410bcc59271f217d74b419b088c1ca1359d0f Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 26 Nov 2025 18:59:18 +0100
Subject: [PATCH 2/9] Add missing enumerators

---
 clang/lib/CodeGen/CGDecl.cpp | 3 +++
 clang/lib/CodeGen/CGStmt.cpp | 7 +++++++
 2 files changed, 10 insertions(+)

diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8b1cd83af2396..7c96e38908267 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -143,6 +143,9 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
     // None of these decls require codegen support.
     return;
 
+  case Decl::CXXExpansionStmt:
+    llvm_unreachable("TODO");
+
   case Decl::NamespaceAlias:
     if (CGDebugInfo *DI = getDebugInfo())
         DI->EmitNamespaceAlias(cast<NamespaceAliasDecl>(D));
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 36be3295950b8..40c92035008ae 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -204,6 +204,13 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
   case Stmt::CXXForRangeStmtClass:
     EmitCXXForRangeStmt(cast<CXXForRangeStmt>(*S), Attrs);
     break;
+  case Stmt::CXXEnumeratingExpansionStmtPatternClass:
+  case Stmt::CXXIteratingExpansionStmtPatternClass:
+  case Stmt::CXXDestructuringExpansionStmtPatternClass:
+  case Stmt::CXXDependentExpansionStmtPatternClass:
+    llvm_unreachable("unexpanded expansion statements should not be emitted");
+  case Stmt::CXXExpansionStmtInstantiationClass:
+    llvm_unreachable("Todo");
   case Stmt::SEHTryStmtClass:
     EmitSEHTryStmt(cast<SEHTryStmt>(*S));
     break;

>From f812c54d41167234340df355aa3645e5ba7a1fd2 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 1 Dec 2025 18:25:45 +0100
Subject: [PATCH 3/9] Documentation improvements

---
 clang/include/clang/AST/Decl.h         |  2 +-
 clang/include/clang/AST/DeclTemplate.h | 32 ++++++++++++++++------
 clang/include/clang/AST/ExprCXX.h      | 13 +++++++++
 clang/include/clang/AST/StmtCXX.h      | 38 +++++++++++++++++---------
 4 files changed, 63 insertions(+), 22 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index b8f8e002ebcce..e15f4dcd3a0bc 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1247,7 +1247,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
 
   /// Returns true for local variable declarations other than parameters.
   /// Note that this includes static variables inside of functions. It also
-  /// includes variables inside blocks.
+  /// includes variables inside blocks and expansion statements.
   ///
   ///   void foo() { int x; static int y; extern int z; }
   bool isLocalVarDecl() const {
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 45a58b6589074..6f4adf076a0a2 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3375,7 +3375,18 @@ class TemplateParamObjectDecl : public ValueDecl,
 /// 'CXXExpansionStmtInstantiation' is. The latter is also what's used for
 /// codegen and constant evaluation.
 ///
-/// For example, if the user writes the following expansion statement:
+/// There are three kinds of expansion statements; they correspond to three
+/// derived classes of 'CXXExpansionStmtPattern'. There is also a fourth derived
+/// class that is used if we don't know what kind of expansion statement we're
+/// dealing with (because the thing we're expanding is dependent). See the
+/// comment on those classes for more information about how they work:
+///
+///   1. CXXEnumeratingExpansionStmtPattern
+///   2. CXXIteratingExpansionStmtPattern
+///   3. CXXDestructuringExpansionStmtPattern
+///   4. CXXDependentExpansionStmtPattern
+///
+/// As an example, if the user writes the following expansion statement:
 /// \verbatim
 ///   std::tuple<int, int, int> a{1, 2, 3};
 ///   template for (auto x : a) {
@@ -3389,28 +3400,33 @@ class TemplateParamObjectDecl : public ValueDecl,
 /// 'a'.
 ///
 /// After expansion, we end up with a 'CXXExpansionStmtInstantiation' that
-/// contains a DecompositionDecl and 3 CompoundStmts, one for each expansion:
+/// is *equivalent* to the AST shown below. Note that only the inner '{}' (i.e.
+/// those marked as 'Actual "CompoundStmt"' below) are actually present as
+/// 'CompoundStmt's in the AST; the outer braces that wrap everything do *not*
+/// correspond to an actual 'CompoundStmt' and are implicit in the sense that we
+/// simply push a scope when evaluating or emitting IR for a
+/// 'CXXExpansionStmtInstantiation'.
 ///
 /// \verbatim
-/// {
+/// { // Not actually present in the AST.
 ///   auto [__u0, __u1, __u2] = a;
-///   {
+///   { // Actual 'CompoundStmt'.
 ///     auto x = __u0;
 ///     // ...
 ///   }
-///   {
+///   { // Actual 'CompoundStmt'.
 ///     auto x = __u1;
 ///     // ...
 ///   }
-///   {
+///   { // Actual 'CompoundStmt'.
 ///     auto x = __u2;
 ///     // ...
 ///   }
 /// }
 /// \endverbatim
 ///
-/// The outer braces shown above are implicit; we don't actually create another
-/// CompoundStmt wrapping everything.
+/// See the documentation around 'CXXExpansionStmtInstantiation' for more notes
+/// as to why this node exist and how it is used.
 ///
 /// \see CXXExpansionStmtPattern
 /// \see CXXExpansionStmtInstantiation
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 668a51dad0ae9..a190a178bfce8 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5501,6 +5501,19 @@ class BuiltinBitCastExpr final
 
 /// Represents an expansion-init-list of an enumerating expansion statement.
 ///
+/// For example, in
+/// \verbatim
+///   template for (auto x : { 1, 2, 3 }) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// the '{ 1, 2, 3 }' part is parsed and stored as a 'CXXExpansionInitListExpr';
+/// syntactically, this *looks* very similar to an initializer list, but it
+/// isn't actually an expression: '{ 1, 2, 3 }' as a whole is never evaluated
+/// or emitted, only the individual expressions '1', '2', and '3' are. We still
+/// represent it as an expression in the AST for simplicity.
+///
 /// \see CXXEnumeratingExpansionStmtPattern
 class CXXExpansionInitListExpr final
     : public Expr,
diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index 96c3f912d6c3e..2ecb653126e5f 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -533,11 +533,7 @@ class CoreturnStmt : public Stmt {
 /// subexpressions required by its derived classes. This is to simplify the
 /// implementation of 'children()' and friends.
 ///
-/// \see CXXExpansionStmtDecl
-/// \see CXXEnumeratingExpansionStmtPattern
-/// \see CXXIteratingExpansionStmtPattern
-/// \see CXXDestructuringExpansionStmtPattern
-/// \see CXXDependentExpansionStmtPattern
+/// \see CXXExpansionStmtDecl for more documentation on expansion statements.
 class CXXExpansionStmtPattern : public Stmt {
   friend class ASTStmtReader;
 
@@ -878,25 +874,28 @@ class CXXDestructuringExpansionStmtPattern : public CXXExpansionStmtPattern {
 /// Represents the code generated for an expanded expansion statement.
 ///
 /// This holds 'shared statements' and 'instantiations'; these encode the
-/// general underlying pattern that all expansion statements desugar to:
+/// general underlying pattern that all expansion statements desugar to. Note
+/// that only the inner '{}' (i.e. those marked as 'Actual "CompoundStmt"'
+/// below) are actually present as 'CompoundStmt's in the AST; the outer braces
+/// that wrap everything do *not* correspond to an actual 'CompoundStmt' and are
+/// implicit in the sense that we simply push a scope when evaluating or
+/// emitting IR for a 'CXXExpansionStmtInstantiation'.
+///
+/// The 'instantiations' are precisely these inner compound statements.
 ///
 /// \verbatim
-/// {
+/// { // Not actually present in the AST.
 ///   <shared statements>
-///   {
+///   { // Actual 'CompoundStmt'.
 ///     <1st instantiation>
 ///   }
 ///   ...
-///   {
+///   { // Actual 'CompoundStmt'.
 ///     <n-th instantiation>
 ///   }
 /// }
 /// \endverbatim
 ///
-/// Here, the only thing that is stored in the AST are the 'shared statements'
-/// and the 'CompoundStmt's that wrap the 'instantiations'. The outer braces
-/// shown above are implicit.
-///
 /// For example, the CXXExpansionStmtInstantiation that corresponds to the
 /// following expansion statement
 ///
@@ -926,6 +925,19 @@ class CXXDestructuringExpansionStmtPattern : public CXXExpansionStmtPattern {
 ///   }
 /// }
 /// \endverbatim
+///
+/// There are two reasons why this needs to exist and why we don't just store a
+/// list of instantiations in some other node:
+///
+///   1. We need custom codegen to handle break/continue in expansion statements
+///      properly, so it can't just be a compound statement.
+///
+///   2. The expansions are created after both the pattern and the
+///      'CXXExpansionStmtDecl', so we can't just store them as trailing data in
+///      either of those nodes (because we don't know how many expansions there
+///      will be when those notes are allocated).
+///
+/// \see CXXExpansionStmtDecl
 class CXXExpansionStmtInstantiation final
     : public Stmt,
       llvm::TrailingObjects<CXXExpansionStmtInstantiation, Stmt *> {

>From ec6fde37824bad20fa902aab4e4ae36c907092c0 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 1 Dec 2025 19:13:34 +0100
Subject: [PATCH 4/9] Fix copy-paste error and only check for instantiation
 dependence

---
 clang/lib/AST/StmtCXX.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index 87bf376f3770d..f34c11d4eff39 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -171,9 +171,8 @@ bool CXXExpansionStmtPattern::hasDependentSize() const {
 
   if (auto *Iterating = dyn_cast<CXXIteratingExpansionStmtPattern>(this)) {
     const Expr *Begin = Iterating->getBeginVar()->getInit();
-    const Expr *End = Iterating->getBeginVar()->getInit();
-    return Begin->isTypeDependent() || Begin->isValueDependent() ||
-           End->isTypeDependent() || End->isValueDependent();
+    const Expr *End = Iterating->getEndVar()->getInit();
+    return Begin->isInstantiationDependent() || End->isInstantiationDependent();
   }
 
   if (isa<CXXDestructuringExpansionStmtPattern>(this))

>From 4415e4dbdfe833269586c7db50d8516629a59315 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 1 Dec 2025 21:29:18 +0100
Subject: [PATCH 5/9] Do not create a template parameter list for the index

---
 clang/include/clang/AST/DeclTemplate.h    | 12 ++++++------
 clang/lib/AST/ASTImporter.cpp             |  4 ++--
 clang/lib/AST/DeclTemplate.cpp            | 13 ++++++-------
 clang/lib/Serialization/ASTReaderDecl.cpp |  2 +-
 clang/lib/Serialization/ASTWriterDecl.cpp |  2 +-
 5 files changed, 16 insertions(+), 17 deletions(-)

diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 6f4adf076a0a2..d0fbbabe94aa2 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3432,18 +3432,18 @@ class TemplateParamObjectDecl : public ValueDecl,
 /// \see CXXExpansionStmtInstantiation
 class CXXExpansionStmtDecl : public Decl, public DeclContext {
   CXXExpansionStmtPattern *Expansion = nullptr;
-  TemplateParameterList *TParams;
+  NonTypeTemplateParmDecl *IndexNTTP = nullptr;
   CXXExpansionStmtInstantiation *Instantiations = nullptr;
 
   CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
-                       TemplateParameterList *TParams);
+                       NonTypeTemplateParmDecl *NTTP);
 
 public:
   friend class ASTDeclReader;
 
   static CXXExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC,
                                       SourceLocation Loc,
-                                      TemplateParameterList *TParams);
+                                      NonTypeTemplateParmDecl *NTTP);
   static CXXExpansionStmtDecl *CreateDeserialized(ASTContext &C,
                                                   GlobalDeclID ID);
 
@@ -3462,10 +3462,10 @@ class CXXExpansionStmtDecl : public Decl, public DeclContext {
     Instantiations = S;
   }
 
-  NonTypeTemplateParmDecl *getIndexTemplateParm() const {
-    return cast<NonTypeTemplateParmDecl>(TParams->getParam(0));
+  NonTypeTemplateParmDecl *getIndexTemplateParm() { return IndexNTTP; }
+  const NonTypeTemplateParmDecl *getIndexTemplateParm() const {
+    return IndexNTTP;
   }
-  TemplateParameterList *getTemplateParameters() const { return TParams; }
 
   SourceRange getSourceRange() const override LLVM_READONLY;
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index b94c18e60ba58..225c573ecc042 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -2879,14 +2879,14 @@ ASTNodeImporter::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
   Error Err = Error::success();
   auto ToLocation = importChecked(Err, D->getLocation());
   auto ToExpansion = importChecked(Err, D->getExpansionPattern());
-  auto ToTemplateParams = importChecked(Err, D->getTemplateParameters());
+  auto ToIndex = importChecked(Err, D->getIndexTemplateParm());
   auto ToInstantiations = importChecked(Err, D->getInstantiations());
   if (Err)
     return std::move(Err);
 
   CXXExpansionStmtDecl *ToD;
   if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, ToLocation,
-                              ToTemplateParams))
+                              ToIndex))
     return ToD;
 
   ToD->setExpansionPattern(ToExpansion);
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index edfd5cf91196e..a770112409486 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1718,9 +1718,8 @@ clang::getReplacedTemplateParameter(Decl *D, unsigned Index) {
         cast<FunctionDecl>(D)->getTemplateSpecializationInfo()->getTemplate(),
         Index);
   case Decl::Kind::CXXExpansionStmt:
-    return {
-        cast<CXXExpansionStmtDecl>(D)->getTemplateParameters()->getParam(Index),
-        {}};
+    assert(Index == 0 && "expansion stmts only have a single template param");
+    return {cast<CXXExpansionStmtDecl>(D)->getIndexTemplateParm(), {}};
   default:
     llvm_unreachable("Unhandled templated declaration kind");
   }
@@ -1794,14 +1793,14 @@ const Decl &clang::adjustDeclToTemplate(const Decl &D) {
 }
 
 CXXExpansionStmtDecl::CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
-                                           TemplateParameterList *TParams)
+                                           NonTypeTemplateParmDecl *NTTP)
     : Decl(CXXExpansionStmt, DC, Loc), DeclContext(CXXExpansionStmt),
-      TParams(TParams) {}
+      IndexNTTP(NTTP) {}
 
 CXXExpansionStmtDecl *
 CXXExpansionStmtDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation Loc,
-                             TemplateParameterList *TParams) {
-  return new (C, DC) CXXExpansionStmtDecl(DC, Loc, TParams);
+                             NonTypeTemplateParmDecl *NTTP) {
+  return new (C, DC) CXXExpansionStmtDecl(DC, Loc, NTTP);
 }
 CXXExpansionStmtDecl *
 CXXExpansionStmtDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 3086d7315a862..e8da8a3060904 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2775,7 +2775,7 @@ void ASTDeclReader::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
   D->Expansion = cast<CXXExpansionStmtPattern>(Record.readStmt());
   D->Instantiations =
       cast_or_null<CXXExpansionStmtInstantiation>(Record.readStmt());
-  D->TParams = Record.readTemplateParameterList();
+  D->IndexNTTP = cast<NonTypeTemplateParmDecl>(Record.readDeclRef());
 }
 
 void ASTDeclReader::VisitEmptyDecl(EmptyDecl *D) {
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 23d11341d131a..c65c9f8fc581f 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2193,7 +2193,7 @@ void ASTDeclWriter::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
   VisitDecl(D);
   Record.AddStmt(D->getExpansionPattern());
   Record.AddStmt(D->getInstantiations());
-  Record.AddTemplateParameterList(D->getTemplateParameters());
+  Record.AddDeclRef(D->getIndexTemplateParm());
   Code = serialization::DECL_EXPANSION_STMT;
 }
 

>From 8a4a548b2ec5d4fa9cc0b0bec96e324f8745c4f2 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 3 Dec 2025 18:17:15 +0100
Subject: [PATCH 6/9] Eliminate CXXExpansionInitListExpr

---
 clang/include/clang/AST/ComputeDependence.h   |  3 -
 clang/include/clang/AST/ExprCXX.h             | 85 ++-----------------
 clang/include/clang/AST/RecursiveASTVisitor.h |  1 -
 clang/include/clang/AST/StmtCXX.h             |  6 +-
 clang/include/clang/AST/TextNodeDumper.h      |  1 -
 clang/include/clang/Basic/StmtNodes.td        |  1 -
 .../include/clang/Serialization/ASTBitCodes.h |  1 -
 clang/lib/AST/ASTImporter.cpp                 | 17 ----
 clang/lib/AST/ComputeDependence.cpp           |  7 --
 clang/lib/AST/Expr.cpp                        |  1 -
 clang/lib/AST/ExprCXX.cpp                     | 36 +-------
 clang/lib/AST/ExprClassification.cpp          |  1 -
 clang/lib/AST/ExprConstant.cpp                |  1 -
 clang/lib/AST/ItaniumMangle.cpp               |  1 -
 clang/lib/AST/StmtCXX.cpp                     | 22 -----
 clang/lib/AST/StmtPrinter.cpp                 |  7 --
 clang/lib/AST/StmtProfile.cpp                 |  5 --
 clang/lib/AST/TextNodeDumper.cpp              |  6 --
 clang/lib/Sema/SemaExceptionSpec.cpp          |  1 -
 clang/lib/Serialization/ASTReaderStmt.cpp     | 17 +---
 clang/lib/Serialization/ASTWriterStmt.cpp     | 10 ---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |  1 -
 clang/tools/libclang/CXCursor.cpp             |  1 -
 23 files changed, 10 insertions(+), 222 deletions(-)

diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h
index 792f45bea5aeb..c298f2620f211 100644
--- a/clang/include/clang/AST/ComputeDependence.h
+++ b/clang/include/clang/AST/ComputeDependence.h
@@ -94,7 +94,6 @@ class DesignatedInitExpr;
 class ParenListExpr;
 class PseudoObjectExpr;
 class AtomicExpr;
-class CXXExpansionInitListExpr;
 class ArraySectionExpr;
 class OMPArrayShapingExpr;
 class OMPIteratorExpr;
@@ -192,8 +191,6 @@ 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/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a190a178bfce8..6bf2d52399285 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5499,79 +5499,6 @@ class BuiltinBitCastExpr final
   }
 };
 
-/// Represents an expansion-init-list of an enumerating expansion statement.
-///
-/// For example, in
-/// \verbatim
-///   template for (auto x : { 1, 2, 3 }) {
-///     // ...
-///   }
-/// \endverbatim
-///
-/// the '{ 1, 2, 3 }' part is parsed and stored as a 'CXXExpansionInitListExpr';
-/// syntactically, this *looks* very similar to an initializer list, but it
-/// isn't actually an expression: '{ 1, 2, 3 }' as a whole is never evaluated
-/// or emitted, only the individual expressions '1', '2', and '3' are. We still
-/// represent it as an expression in the AST for simplicity.
-///
-/// \see CXXEnumeratingExpansionStmtPattern
-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.
 ///
@@ -5585,17 +5512,17 @@ class CXXExpansionInitListSelectExpr : public Expr {
 public:
   CXXExpansionInitListSelectExpr(EmptyShell Empty);
   CXXExpansionInitListSelectExpr(const ASTContext &C,
-                                 CXXExpansionInitListExpr *Range, Expr *Idx);
+                                 InitListExpr *Range, Expr *Idx);
 
-  CXXExpansionInitListExpr *getRangeExpr() {
-    return cast<CXXExpansionInitListExpr>(SubExprs[RANGE]);
+  InitListExpr *getRangeExpr() {
+    return cast<InitListExpr>(SubExprs[RANGE]);
   }
 
-  const CXXExpansionInitListExpr *getRangeExpr() const {
-    return cast<CXXExpansionInitListExpr>(SubExprs[RANGE]);
+  const InitListExpr *getRangeExpr() const {
+    return cast<InitListExpr>(SubExprs[RANGE]);
   }
 
-  void setRangeExpr(CXXExpansionInitListExpr *E) { SubExprs[RANGE] = E; }
+  void setRangeExpr(InitListExpr *E) { SubExprs[RANGE] = E; }
 
   Expr *getIndexExpr() { return SubExprs[INDEX]; }
   const Expr *getIndexExpr() const { return SubExprs[INDEX]; }
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 24052df70c7a8..b325df7328f76 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3130,7 +3130,6 @@ DEF_TRAVERSE_STMT(CXXIteratingExpansionStmtPattern, {})
 DEF_TRAVERSE_STMT(CXXDestructuringExpansionStmtPattern, {})
 DEF_TRAVERSE_STMT(CXXDependentExpansionStmtPattern, {})
 DEF_TRAVERSE_STMT(CXXExpansionStmtInstantiation, {})
-DEF_TRAVERSE_STMT(CXXExpansionInitListExpr, {})
 DEF_TRAVERSE_STMT(CXXExpansionInitListSelectExpr, {})
 DEF_TRAVERSE_STMT(CXXDestructuringExpansionSelectExpr, {})
 
diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index 2ecb653126e5f..f796f317c363e 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -586,8 +586,6 @@ class CXXExpansionStmtPattern : public Stmt {
     return getBody() ? getBody()->getEndLoc() : RParenLoc;
   }
 
-  bool hasDependentSize() const;
-
   CXXExpansionStmtDecl *getDecl() { return ParentDecl; }
   const CXXExpansionStmtDecl *getDecl() const { return ParentDecl; }
 
@@ -632,7 +630,7 @@ class CXXExpansionStmtPattern : public Stmt {
 /// an initializer list, but it isn't actually an expression in and of itself
 /// (in that it is never evaluated or emitted) and instead is just treated as
 /// a group of expressions. The expansion initializer of this is always a
-/// 'CXXExpansionInitListExpr'.
+/// syntactic-form 'InitListExpr'.
 ///
 /// Example:
 /// \verbatim
@@ -644,7 +642,7 @@ class CXXExpansionStmtPattern : public Stmt {
 /// Note that the expression-list may also contain pack expansions, e.g.
 /// '{ 1, xs... }', in which case the expansion size is dependent.
 ///
-/// Here, the '{ 1, 2, 3 }' is parsed as a 'CXXExpansionInitListExpr'. This node
+/// Here, the '{ 1, 2, 3 }' is parsed as an 'InitListExpr'. This node
 /// handles storing (and pack-expanding) the individual expressions.
 ///
 /// Sema then wraps this with a 'CXXExpansionInitListSelectExpr', which also
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 3db8ce0d5aed3..64998c128053a 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -314,7 +314,6 @@ class TextNodeDumper
   VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *Node);
   void VisitCXXDestructuringExpansionSelectExpr(
       const CXXDestructuringExpansionSelectExpr *Node);
-  void VisitCXXExpansionInitListExpr(const CXXExpansionInitListExpr *Node);
   void VisitObjCAtCatchStmt(const ObjCAtCatchStmt *Node);
   void VisitObjCEncodeExpr(const ObjCEncodeExpr *Node);
   void VisitObjCMessageExpr(const ObjCMessageExpr *Node);
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index 505ed481e0895..abbde50c7016b 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -187,7 +187,6 @@ def ConceptSpecializationExpr : StmtNode<Expr>;
 def RequiresExpr : StmtNode<Expr>;
 
 // C++26 Expansion statement support expressions
-def CXXExpansionInitListExpr : StmtNode<Expr>;
 def CXXExpansionInitListSelectExpr : StmtNode<Expr>;
 def CXXDestructuringExpansionSelectExpr : StmtNode<Expr>;
 
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 36cccdcbd306e..033407dbba2f4 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1942,7 +1942,6 @@ enum StmtCode {
   EXPR_CXX_FOLD,                           // CXXFoldExpr
   EXPR_CONCEPT_SPECIALIZATION,             // ConceptSpecializationExpr
   EXPR_REQUIRES,                           // RequiresExpr
-  EXPR_CXX_EXPANSION_INIT_LIST,            // CXXExpansionInitListExpr
   EXPR_CXX_EXPANSION_INIT_LIST_SELECT,     // CXXExpansionInitListSelectExpr
   EXPR_CXX_DESTRUCTURING_EXPANSION_SELECT, // CXXDestructuringExpansionSelectExpr
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 225c573ecc042..0b65ec4f4cd8c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -711,7 +711,6 @@ namespace clang {
     VisitSubstNonTypeTemplateParmPackExpr(SubstNonTypeTemplateParmPackExpr *E);
     ExpectedStmt VisitPseudoObjectExpr(PseudoObjectExpr *E);
     ExpectedStmt VisitCXXParenListInitExpr(CXXParenListInitExpr *E);
-    ExpectedStmt VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E);
     ExpectedStmt
     VisitCXXExpansionInitListSelectExpr(CXXExpansionInitListSelectExpr *E);
     ExpectedStmt VisitCXXDestructuringExpansionSelectExpr(
@@ -9480,22 +9479,6 @@ ASTNodeImporter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
                                       ToInitLoc, ToBeginLoc, ToEndLoc);
 }
 
-ExpectedStmt
-ASTNodeImporter::VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E) {
-  Error Err = Error::success();
-  SmallVector<Expr *> ToExprs;
-  auto ToLBraceLoc = importChecked(Err, E->getLBraceLoc());
-  auto ToRBraceLoc = importChecked(Err, E->getRBraceLoc());
-  for (Expr *FromInst : E->getExprs())
-    ToExprs.push_back(importChecked(Err, FromInst));
-
-  if (Err)
-    return std::move(Err);
-
-  return CXXExpansionInitListExpr::Create(Importer.getToContext(), ToExprs,
-                                          ToLBraceLoc, ToRBraceLoc);
-}
-
 ExpectedStmt ASTNodeImporter::VisitCXXExpansionInitListSelectExpr(
     CXXExpansionInitListSelectExpr *E) {
   Error Err = Error::success();
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 2ff9d74f1a8d5..638080ea781a9 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -959,10 +959,3 @@ ExprDependence clang::computeDependence(OpenACCAsteriskSizeExpr *E) {
   // way.
   return ExprDependence::None;
 }
-
-ExprDependence clang::computeDependence(CXXExpansionInitListExpr *ILE) {
-  auto D = ExprDependence::None;
-  for (Expr *E : ILE->getExprs())
-    D |= E->getDependence();
-  return D;
-}
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index c61660c90513f..af2d264623f89 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3689,7 +3689,6 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
   case RecoveryExprClass:
   case CXXFoldExprClass:
   case CXXExpansionInitListSelectExprClass:
-  case CXXExpansionInitListExprClass:
   case CXXDestructuringExpansionSelectExprClass:
     // Make a conservative assumption for dependent nodes.
     return IncludePossibleEffects;
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 7ba49f74c1f7d..8975d9af87ce1 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -2021,40 +2021,11 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
   setDependence(computeDependence(this));
 }
 
-CXXExpansionInitListExpr::CXXExpansionInitListExpr(EmptyShell ES,
-                                                   unsigned NumExprs)
-    : Expr(CXXExpansionInitListExprClass, ES), NumExprs(NumExprs) {}
-
-CXXExpansionInitListExpr::CXXExpansionInitListExpr(ArrayRef<Expr *> Exprs,
-                                                   SourceLocation LBraceLoc,
-                                                   SourceLocation RBraceLoc)
-    : Expr(CXXExpansionInitListExprClass, QualType(), VK_PRValue, OK_Ordinary),
-      NumExprs(static_cast<unsigned>(Exprs.size())), LBraceLoc(LBraceLoc),
-      RBraceLoc(RBraceLoc) {
-  llvm::uninitialized_copy(Exprs, getTrailingObjects());
-  setDependence(computeDependence(this));
-}
-
-CXXExpansionInitListExpr *
-CXXExpansionInitListExpr::Create(const ASTContext &C, ArrayRef<Expr *> Exprs,
-                                 SourceLocation LBraceLoc,
-                                 SourceLocation RBraceLoc) {
-  void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(Exprs.size()));
-  return new (Mem) CXXExpansionInitListExpr(Exprs, LBraceLoc, RBraceLoc);
-}
-
-CXXExpansionInitListExpr *
-CXXExpansionInitListExpr::CreateEmpty(const ASTContext &C, EmptyShell Empty,
-                                      unsigned NumExprs) {
-  void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(NumExprs));
-  return new (Mem) CXXExpansionInitListExpr(Empty, NumExprs);
-}
-
 CXXExpansionInitListSelectExpr::CXXExpansionInitListSelectExpr(EmptyShell Empty)
     : Expr(CXXExpansionInitListSelectExprClass, Empty) {}
 
 CXXExpansionInitListSelectExpr::CXXExpansionInitListSelectExpr(
-    const ASTContext &C, CXXExpansionInitListExpr *Range, Expr *Idx)
+    const ASTContext &C, InitListExpr *Range, Expr *Idx)
     : Expr(CXXExpansionInitListSelectExprClass, C.DependentTy, VK_PRValue,
            OK_Ordinary) {
   setDependence(ExprDependence::TypeValueInstantiation);
@@ -2062,11 +2033,6 @@ CXXExpansionInitListSelectExpr::CXXExpansionInitListSelectExpr(
   SubExprs[INDEX] = Idx;
 }
 
-bool CXXExpansionInitListExpr::containsPackExpansion() const {
-  return llvm::any_of(getExprs(),
-                      [](const Expr *E) { return isa<PackExpansionExpr>(E); });
-}
-
 CXXDestructuringExpansionSelectExpr::CXXDestructuringExpansionSelectExpr(
     EmptyShell Empty)
     : Expr(CXXDestructuringExpansionSelectExprClass, Empty) {}
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index 5521cdf9d04c9..1b139ac0954b8 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -216,7 +216,6 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
   case Expr::SourceLocExprClass:
   case Expr::ConceptSpecializationExprClass:
   case Expr::RequiresExprClass:
-  case Expr::CXXExpansionInitListExprClass:
   case Expr::CXXExpansionInitListSelectExprClass:
   case Expr::CXXDestructuringExpansionSelectExprClass:
     return Cl::CL_PRValue;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index d93f87a27e68d..0bd56bb0a76e2 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20276,7 +20276,6 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
   case Expr::SYCLUniqueStableNameExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::HLSLOutArgExprClass:
-  case Expr::CXXExpansionInitListExprClass:
   case Expr::CXXExpansionInitListSelectExprClass:
   case Expr::CXXDestructuringExpansionSelectExprClass:
     return ICEDiag(IK_NotICE, E->getBeginLoc());
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 28883edc34e6d..db8cdc0a33d04 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -4946,7 +4946,6 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   case Expr::CXXParenListInitExprClass:
   case Expr::PackIndexingExprClass:
   case Expr::CXXExpansionInitListSelectExprClass:
-  case Expr::CXXExpansionInitListExprClass:
   case Expr::CXXDestructuringExpansionSelectExprClass:
     llvm_unreachable("unexpected statement kind");
 
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index f34c11d4eff39..da4e3be6eaa2f 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -162,28 +162,6 @@ VarDecl *CXXExpansionStmtPattern::getExpansionVariable() {
   return cast<VarDecl>(LV);
 }
 
-bool CXXExpansionStmtPattern::hasDependentSize() const {
-  if (isa<CXXEnumeratingExpansionStmtPattern>(this))
-    return cast<CXXExpansionInitListSelectExpr>(
-               getExpansionVariable()->getInit())
-        ->getRangeExpr()
-        ->containsPackExpansion();
-
-  if (auto *Iterating = dyn_cast<CXXIteratingExpansionStmtPattern>(this)) {
-    const Expr *Begin = Iterating->getBeginVar()->getInit();
-    const Expr *End = Iterating->getEndVar()->getInit();
-    return Begin->isInstantiationDependent() || End->isInstantiationDependent();
-  }
-
-  if (isa<CXXDestructuringExpansionStmtPattern>(this))
-    return false;
-
-  if (isa<CXXDependentExpansionStmtPattern>(this))
-    return true;
-
-  llvm_unreachable("Invalid expansion statement class");
-}
-
 CXXIteratingExpansionStmtPattern::CXXIteratingExpansionStmtPattern(
     EmptyShell Empty)
     : CXXExpansionStmtPattern(CXXIteratingExpansionStmtPatternClass, Empty) {}
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 6760d3bbe54cc..165bbd046f5f4 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -490,13 +490,6 @@ void StmtPrinter::VisitCXXExpansionStmtInstantiation(
   llvm_unreachable("should never be printed");
 }
 
-void StmtPrinter::VisitCXXExpansionInitListExpr(
-    CXXExpansionInitListExpr *Node) {
-  OS << "{ ";
-  llvm::interleaveComma(Node->getExprs(), OS, [&](Expr *E) { PrintExpr(E); });
-  OS << " }";
-}
-
 void StmtPrinter::VisitCXXExpansionInitListSelectExpr(
     CXXExpansionInitListSelectExpr *Node) {
   PrintExpr(Node->getRangeExpr());
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 45513c479e1d1..08759bbaa2ed3 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2431,11 +2431,6 @@ void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) {
 
 void StmtProfiler::VisitEmbedExpr(const EmbedExpr *E) { VisitExpr(E); }
 
-void StmtProfiler::VisitCXXExpansionInitListExpr(
-    const CXXExpansionInitListExpr *E) {
-  VisitExpr(E);
-}
-
 void StmtProfiler::VisitCXXExpansionInitListSelectExpr(
     const CXXExpansionInitListSelectExpr *E) {
   VisitExpr(E);
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 4d19bf63f3536..e20cb63ee54b8 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1836,12 +1836,6 @@ void TextNodeDumper::VisitCXXDependentScopeMemberExpr(
   OS << " " << (Node->isArrow() ? "->" : ".") << Node->getMember();
 }
 
-void TextNodeDumper::VisitCXXExpansionInitListExpr(
-    const CXXExpansionInitListExpr *Node) {
-  if (Node->containsPackExpansion())
-    OS << " contains_pack";
-}
-
 void TextNodeDumper::VisitCXXDestructuringExpansionSelectExpr(
     const CXXDestructuringExpansionSelectExpr *Node) {
   dumpDeclRef(Node->getDecompositionDecl());
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index a56f933638190..5839785dbaab2 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1289,7 +1289,6 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::VAArgExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::CXXExpansionInitListSelectExprClass:
-  case Expr::CXXExpansionInitListExprClass:
   case Expr::CXXDestructuringExpansionSelectExprClass:
     return canSubStmtsThrow(*this, S);
 
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index c74113c4ab0da..f3ea8c99666eb 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1777,20 +1777,10 @@ void ASTStmtReader::VisitCXXDependentExpansionStmtPattern(
   S->setExpansionInitializer(Record.readSubExpr());
 }
 
-void ASTStmtReader::VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E) {
-  VisitExpr(E);
-  assert(Record.peekInt() == E->getNumExprs() && "NumExprFields is wrong ?");
-  Record.skipInts(1);
-  E->LBraceLoc = readSourceLocation();
-  E->RBraceLoc = readSourceLocation();
-  for (unsigned I = 0; I < E->getNumExprs(); ++I)
-    E->getExprs()[I] = Record.readSubExpr();
-}
-
 void ASTStmtReader::VisitCXXExpansionInitListSelectExpr(
     CXXExpansionInitListSelectExpr *E) {
   VisitExpr(E);
-  E->setRangeExpr(cast<CXXExpansionInitListExpr>(Record.readSubExpr()));
+  E->setRangeExpr(cast<InitListExpr>(Record.readSubExpr()));
   E->setIndexExpr(Record.readSubExpr());
 }
 
@@ -4537,11 +4527,6 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
     }
 
-    case EXPR_CXX_EXPANSION_INIT_LIST:
-      S = CXXExpansionInitListExpr::CreateEmpty(
-          Context, Empty, Record[ASTStmtReader::NumExprFields]);
-      break;
-
     case EXPR_CXX_EXPANSION_INIT_LIST_SELECT:
       S = new (Context) CXXExpansionInitListSelectExpr(Empty);
       break;
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index d795b23525888..8319a59bc951f 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1757,16 +1757,6 @@ void ASTStmtWriter::VisitCXXDependentExpansionStmtPattern(
   Code = serialization::STMT_CXX_DEPENDENT_EXPANSION;
 }
 
-void ASTStmtWriter::VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E) {
-  VisitExpr(E);
-  Record.push_back(E->getNumExprs());
-  Record.AddSourceLocation(E->getLBraceLoc());
-  Record.AddSourceLocation(E->getRBraceLoc());
-  for (Expr *SubExpr : E->getExprs())
-    Record.AddStmt(SubExpr);
-  Code = serialization::EXPR_CXX_EXPANSION_INIT_LIST;
-}
-
 void ASTStmtWriter::VisitCXXExpansionInitListSelectExpr(
     CXXExpansionInitListSelectExpr *E) {
   VisitExpr(E);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 38106f154c15e..53f85c22dd020 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1757,7 +1757,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::CXXDestructuringExpansionStmtPatternClass:
     case Stmt::CXXDependentExpansionStmtPatternClass:
     case Stmt::CXXExpansionStmtInstantiationClass:
-    case Stmt::CXXExpansionInitListExprClass:
     case Stmt::CXXExpansionInitListSelectExprClass:
     case Stmt::CXXDestructuringExpansionSelectExprClass:
     case Stmt::OMPCanonicalLoopClass:
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index f0ae4f0b593a2..ecd1f86c3ddb6 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -343,7 +343,6 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
   case Stmt::EmbedExprClass:
   case Stmt::HLSLOutArgExprClass:
   case Stmt::OpenACCAsteriskSizeExprClass:
-  case Stmt::CXXExpansionInitListExprClass:
   case Stmt::CXXExpansionInitListSelectExprClass:
   case Stmt::CXXDestructuringExpansionSelectExprClass:
     K = CXCursor_UnexposedExpr;

>From 76330f99eaed92260746a95dfe08b8496c14f7c3 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 3 Dec 2025 20:14:37 +0100
Subject: [PATCH 7/9] Merge all pattern kinds into a single AST node

---
 clang/include/clang/AST/RecursiveASTVisitor.h |   5 +-
 clang/include/clang/AST/StmtCXX.h             | 495 +++++++++---------
 clang/include/clang/AST/TextNodeDumper.h      |   1 +
 clang/include/clang/Basic/StmtNodes.td        |   6 +-
 .../include/clang/Serialization/ASTBitCodes.h |  15 +-
 clang/lib/AST/ASTImporter.cpp                 | 106 ++--
 clang/lib/AST/StmtCXX.cpp                     | 145 ++---
 clang/lib/AST/StmtPrinter.cpp                 |  35 +-
 clang/lib/AST/StmtProfile.cpp                 |  20 -
 clang/lib/AST/TextNodeDumper.cpp              |  18 +
 clang/lib/CodeGen/CGStmt.cpp                  |   5 +-
 clang/lib/Sema/SemaExceptionSpec.cpp          |  10 +-
 clang/lib/Serialization/ASTReaderStmt.cpp     |  50 +-
 clang/lib/Serialization/ASTWriterStmt.cpp     |  36 +-
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |   5 +-
 clang/tools/libclang/CXCursor.cpp             |   5 +-
 16 files changed, 443 insertions(+), 514 deletions(-)

diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index b325df7328f76..b145fa9f9cdf0 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3125,10 +3125,7 @@ DEF_TRAVERSE_STMT(RequiresExpr, {
     TRY_TO(TraverseConceptRequirement(Req));
 })
 
-DEF_TRAVERSE_STMT(CXXEnumeratingExpansionStmtPattern, {})
-DEF_TRAVERSE_STMT(CXXIteratingExpansionStmtPattern, {})
-DEF_TRAVERSE_STMT(CXXDestructuringExpansionStmtPattern, {})
-DEF_TRAVERSE_STMT(CXXDependentExpansionStmtPattern, {})
+DEF_TRAVERSE_STMT(CXXExpansionStmtPattern, {})
 DEF_TRAVERSE_STMT(CXXExpansionStmtInstantiation, {})
 DEF_TRAVERSE_STMT(CXXExpansionInitListSelectExpr, {})
 DEF_TRAVERSE_STMT(CXXDestructuringExpansionSelectExpr, {})
diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index f796f317c363e..fcc30c3b1d421 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -525,107 +525,16 @@ class CoreturnStmt : public Stmt {
   }
 };
 
-/// CXXExpansionStmtPattern - Base class for an unexpanded C++ expansion
-/// statement.
+/// CXXExpansionStmtPattern - Represents an unexpanded C++ expansion statement.
 ///
-/// The main purpose for this class is to store the AST nodes common to all
-/// variants of expansion statements; it also provides storage for additional
-/// subexpressions required by its derived classes. This is to simplify the
-/// implementation of 'children()' and friends.
+/// There are four kinds of expansion statements.
 ///
-/// \see CXXExpansionStmtDecl for more documentation on expansion statements.
-class CXXExpansionStmtPattern : public Stmt {
-  friend class ASTStmtReader;
-
-  CXXExpansionStmtDecl *ParentDecl;
-  SourceLocation LParenLoc;
-  SourceLocation ColonLoc;
-  SourceLocation RParenLoc;
-
-protected:
-  enum SubStmt {
-    INIT,
-    VAR,
-    BODY,
-    FIRST_CHILD_STMT,
-
-    // CXXDependentExpansionStmtPattern
-    EXPANSION_INITIALIZER = FIRST_CHILD_STMT,
-    COUNT_CXXDependentExpansionStmtPattern,
-
-    // CXXDestructuringExpansionStmtPattern
-    DECOMP_DECL = FIRST_CHILD_STMT,
-    COUNT_CXXDestructuringExpansionStmtPattern,
-
-    // CXXIteratingExpansionStmtPattern
-    RANGE = FIRST_CHILD_STMT,
-    BEGIN,
-    END,
-    COUNT_CXXIteratingExpansionStmtPattern,
-
-    MAX_COUNT = COUNT_CXXIteratingExpansionStmtPattern,
-  };
-
-  // 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];
-
-  CXXExpansionStmtPattern(StmtClass SC, EmptyShell Empty);
-  CXXExpansionStmtPattern(StmtClass SC, CXXExpansionStmtDecl *ESD, Stmt *Init,
-                          DeclStmt *ExpansionVar, SourceLocation LParenLoc,
-                          SourceLocation ColonLoc, SourceLocation RParenLoc);
-
-public:
-  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;
-  }
-
-  CXXExpansionStmtDecl *getDecl() { return ParentDecl; }
-  const CXXExpansionStmtDecl *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<CXXExpansionStmtPattern *>(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() >= firstCXXExpansionStmtPatternConstant &&
-           T->getStmtClass() <= lastCXXExpansionStmtPatternConstant;
-  }
-
-  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.
+/// 1. Enumerating expansion statements.
+/// 2. Iterating expansion statements.
+/// 3. Destructuring expansion statements.
+/// 4. Dependent expansion statements.
 ///
-/// An 'enumerating' expansion statement is one whose expansion-initializer
+/// 1. An 'enumerating' expansion statement is one whose expansion-initializer
 /// is a brace-enclosed expression-list; this list is syntactically similar to
 /// an initializer list, but it isn't actually an expression in and of itself
 /// (in that it is never evaluated or emitted) and instead is just treated as
@@ -654,30 +563,54 @@ class CXXExpansionStmtPattern : public Stmt {
 /// example, during the 2nd expansion of '{ a, b, c }', I is equal to 1, and
 /// BuildCXXExpansionInitListSelectExpr(), when called via TreeTransform,
 /// 'instantiates' the expression '{ a, b, c }' to just 'b'.
-class CXXEnumeratingExpansionStmtPattern : public CXXExpansionStmtPattern {
-  friend class ASTStmtReader;
-
-public:
-  CXXEnumeratingExpansionStmtPattern(EmptyShell Empty);
-  CXXEnumeratingExpansionStmtPattern(CXXExpansionStmtDecl *ESD, Stmt *Init,
-                                     DeclStmt *ExpansionVar,
-                                     SourceLocation LParenLoc,
-                                     SourceLocation ColonLoc,
-                                     SourceLocation RParenLoc);
-
-  static bool classof(const Stmt *T) {
-    return T->getStmtClass() == CXXEnumeratingExpansionStmtPatternClass;
-  }
-};
-
-/// Represents an expansion statement whose expansion-initializer is
+///
+/// 2. Represents an unexpanded iterating expansion statement.
+///
+/// An 'iterating' expansion statement is one whose expansion-initializer is a
+/// a range (i.e. it has a corresponding 'begin()'/'end()' pair that is
+/// determined based on a number of conditions as stated in [stmt.expand] and
+/// [stmt.ranged]).
+///
+/// The expression used to compute the size of the expansion is not stored and
+/// is only created at the moment of expansion.
+///
+/// Example:
+/// \verbatim
+///   static constexpr std::string_view foo = "1234";
+///   template for (auto x : foo) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// 3. Represents an unexpanded destructuring expansion statement.
+///
+/// A 'destructuring' expansion statement is any expansion statement that is
+/// not enumerating or iterating (i.e. destructuring is the last thing we try,
+/// and if it doesn't work, the program is ill-formed).
+///
+/// This essentially involves treating the expansion-initializer as the
+/// initializer of a structured-binding declarations, with the number of
+/// bindings and expansion size determined by the usual means (array size,
+/// std::tuple_size, etc.).
+///
+/// Example:
+/// \verbatim
+///   std::array<int, 3> a {1, 2, 3};
+///   template for (auto x : a) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// Sema wraps the initializer with a CXXDestructuringExpansionSelectExpr, which
+/// selects a binding based on the current expansion index; this is analogous to
+/// how 'CXXExpansionInitListSelectExpr' is used.
+///
+/// 4. Represents an expansion statement whose expansion-initializer is
 /// type-dependent.
 ///
-/// This will be instantiated as either a 'CXXIteratingExpansionStmtPattern' or
-/// a 'CXXDestructuringExpansionStmtPattern'. Dependent expansion statements can
-/// never be enumerating; those are always stored as a
-/// 'CXXEnumeratingExpansionStmtPattern', even if the expansion size is
-/// dependent because the expression-list contains a pack.
+/// This will be instantiated as either an iterating or destructuring expansion
+/// statement. Dependent expansion statements can never be enumerating, even if
+/// the expansion size is dependent because the expression-list contains a pack.
 ///
 /// Example:
 /// \verbatim
@@ -688,184 +621,278 @@ class CXXEnumeratingExpansionStmtPattern : public CXXExpansionStmtPattern {
 ///     }
 ///   }
 /// \endverbatim
-class CXXDependentExpansionStmtPattern : public CXXExpansionStmtPattern {
+///
+/// \see CXXExpansionStmtDecl for more documentation on expansion statements.
+class CXXExpansionStmtPattern final
+    : public Stmt,
+      llvm::TrailingObjects<CXXExpansionStmtPattern, Stmt *> {
   friend class ASTStmtReader;
+  friend TrailingObjects;
 
 public:
-  CXXDependentExpansionStmtPattern(EmptyShell Empty);
-  CXXDependentExpansionStmtPattern(CXXExpansionStmtDecl *ESD, Stmt *Init,
-                                   DeclStmt *ExpansionVar,
-                                   Expr *ExpansionInitializer,
-                                   SourceLocation LParenLoc,
-                                   SourceLocation ColonLoc,
-                                   SourceLocation RParenLoc);
+  enum class ExpansionStmtKind : uint8_t {
+    Enumerating,
+    Iterating,
+    Destructuring,
+    Dependent,
+  };
 
-  Expr *getExpansionInitializer() {
-    return cast<Expr>(SubStmts[EXPANSION_INITIALIZER]);
-  }
-  const Expr *getExpansionInitializer() const {
-    return cast<Expr>(SubStmts[EXPANSION_INITIALIZER]);
+private:
+  ExpansionStmtKind PatternKind;
+  SourceLocation LParenLoc;
+  SourceLocation ColonLoc;
+  SourceLocation RParenLoc;
+  CXXExpansionStmtDecl *ParentDecl;
+
+  enum SubStmt {
+    INIT,
+    VAR,
+    BODY,
+    FIRST_CHILD_STMT,
+    COUNT_Enumerating = FIRST_CHILD_STMT,
+
+    // Dependent expansion initializer.
+    EXPANSION_INITIALIZER = FIRST_CHILD_STMT,
+    COUNT_Dependent,
+
+    // Destructuring expansion statement.
+    DECOMP_DECL = FIRST_CHILD_STMT,
+    COUNT_Destructuring,
+
+    // Iterating expansion statement.
+    RANGE = FIRST_CHILD_STMT,
+    BEGIN,
+    END,
+    COUNT_Iterating,
+  };
+
+  CXXExpansionStmtPattern(ExpansionStmtKind PatternKind, EmptyShell Empty);
+  CXXExpansionStmtPattern(ExpansionStmtKind PatternKind,
+                          CXXExpansionStmtDecl *ESD, Stmt *Init,
+                          DeclStmt *ExpansionVar, SourceLocation LParenLoc,
+                          SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+public:
+  static CXXExpansionStmtPattern *
+  CreateEmpty(ASTContext &Context, EmptyShell Empty, ExpansionStmtKind Kind);
+
+  /// Create a dependent expansion statement pattern.
+  static CXXExpansionStmtPattern *
+  CreateDependent(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+                  DeclStmt *ExpansionVar, Expr *ExpansionInitializer,
+                  SourceLocation LParenLoc, SourceLocation ColonLoc,
+                  SourceLocation RParenLoc);
+
+  /// Create a destructuring expansion statement pattern.
+  static CXXExpansionStmtPattern *
+  CreateDestructuring(ASTContext &Context, CXXExpansionStmtDecl *ESD,
+                      Stmt *Init, DeclStmt *ExpansionVar,
+                      Stmt *DecompositionDeclStmt, SourceLocation LParenLoc,
+                      SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+  /// Create an enumerating expansion statement pattern.
+  static CXXExpansionStmtPattern *
+  CreateEnumerating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+                    DeclStmt *ExpansionVar, SourceLocation LParenLoc,
+                    SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+  /// Create an iterating expansion statement pattern.
+  static CXXExpansionStmtPattern *
+  CreateIterating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+                  DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin,
+                  DeclStmt *End, SourceLocation LParenLoc,
+                  SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+  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;
   }
-  void setExpansionInitializer(Expr *S) { SubStmts[EXPANSION_INITIALIZER] = S; }
 
-  child_range children() {
-    return child_range(SubStmts,
-                       SubStmts + COUNT_CXXDependentExpansionStmtPattern);
+  ExpansionStmtKind getKind() const { return PatternKind; }
+  bool isDependent() const {
+    return PatternKind == ExpansionStmtKind::Dependent;
+  }
+  bool isEnumerating() const {
+    return PatternKind == ExpansionStmtKind::Enumerating;
+  }
+  bool isIterating() const {
+    return PatternKind == ExpansionStmtKind::Iterating;
+  }
+  bool isDestructuring() const {
+    return PatternKind == ExpansionStmtKind::Destructuring;
   }
 
-  const_child_range children() const {
-    return const_child_range(SubStmts,
-                             SubStmts + COUNT_CXXDependentExpansionStmtPattern);
+  unsigned getNumSubStmts() const { return getNumSubStmts(PatternKind); }
+
+  // Accessors for subcomponents common to all expansion statements.
+  CXXExpansionStmtDecl *getDecl() { return ParentDecl; }
+  const CXXExpansionStmtDecl *getDecl() const { return ParentDecl; }
+
+  Stmt *getInit() { return getSubStmt(INIT); }
+  const Stmt *getInit() const { return getSubStmt(INIT); }
+  void setInit(Stmt *S) { getSubStmt(INIT) = S; }
+
+  VarDecl *getExpansionVariable();
+  const VarDecl *getExpansionVariable() const {
+    return const_cast<CXXExpansionStmtPattern *>(this)->getExpansionVariable();
   }
 
-  static bool classof(const Stmt *T) {
-    return T->getStmtClass() == CXXDependentExpansionStmtPatternClass;
+  DeclStmt *getExpansionVarStmt() { return cast<DeclStmt>(getSubStmt(VAR)); }
+  const DeclStmt *getExpansionVarStmt() const {
+    return cast<DeclStmt>(getSubStmt(VAR));
   }
-};
 
-/// Represents an unexpanded iterating expansion statement.
-///
-/// An 'iterating' expansion statement is one whose expansion-initializer is a
-/// a range (i.e. it has a corresponding 'begin()'/'end()' pair that is
-/// determined based on a number of conditions as stated in [stmt.expand] and
-/// [stmt.ranged]).
-///
-/// The expression used to compute the size of the expansion is not stored and
-/// is only created at the moment of expansion.
-///
-/// Example:
-/// \verbatim
-///   static constexpr std::string_view foo = "1234";
-///   template for (auto x : foo) {
-///     // ...
-///   }
-/// \endverbatim
-class CXXIteratingExpansionStmtPattern : public CXXExpansionStmtPattern {
-  friend class ASTStmtReader;
+  void setExpansionVarStmt(Stmt *S) { getSubStmt(VAR) = S; }
 
-public:
-  CXXIteratingExpansionStmtPattern(EmptyShell Empty);
-  CXXIteratingExpansionStmtPattern(CXXExpansionStmtDecl *ESD, Stmt *Init,
-                                   DeclStmt *ExpansionVar, DeclStmt *Range,
-                                   DeclStmt *Begin, DeclStmt *End,
-                                   SourceLocation LParenLoc,
-                                   SourceLocation ColonLoc,
-                                   SourceLocation RParenLoc);
+  Stmt *getBody() { return getSubStmt(BODY); }
+  const Stmt *getBody() const { return getSubStmt(BODY); }
+  void setBody(Stmt *S) { getSubStmt(BODY) = S; }
 
+  // Accessors for iterating statements.
   const DeclStmt *getRangeVarStmt() const {
-    return cast<DeclStmt>(SubStmts[RANGE]);
+    assert(isIterating());
+    return cast<DeclStmt>(getSubStmt(RANGE));
+  }
+
+  DeclStmt *getRangeVarStmt() {
+    assert(isIterating());
+    return cast<DeclStmt>(getSubStmt(RANGE));
+  }
+
+  void setRangeVarStmt(DeclStmt *S) {
+    assert(isIterating());
+    getSubStmt(RANGE) = S;
   }
-  DeclStmt *getRangeVarStmt() { return cast<DeclStmt>(SubStmts[RANGE]); }
-  void setRangeVarStmt(DeclStmt *S) { SubStmts[RANGE] = S; }
 
   const VarDecl *getRangeVar() const {
+    assert(isIterating());
     return cast<VarDecl>(getRangeVarStmt()->getSingleDecl());
   }
 
   VarDecl *getRangeVar() {
+    assert(isIterating());
     return cast<VarDecl>(getRangeVarStmt()->getSingleDecl());
   }
 
   const DeclStmt *getBeginVarStmt() const {
-    return cast<DeclStmt>(SubStmts[BEGIN]);
+    assert(isIterating());
+    return cast<DeclStmt>(getSubStmt(BEGIN));
+  }
+
+  DeclStmt *getBeginVarStmt() {
+    assert(isIterating());
+    return cast<DeclStmt>(getSubStmt(BEGIN));
+  }
+
+  void setBeginVarStmt(DeclStmt *S) {
+    assert(isIterating());
+    getSubStmt(BEGIN) = S;
   }
-  DeclStmt *getBeginVarStmt() { return cast<DeclStmt>(SubStmts[BEGIN]); }
-  void setBeginVarStmt(DeclStmt *S) { SubStmts[BEGIN] = S; }
 
   const VarDecl *getBeginVar() const {
+    assert(isIterating());
     return cast<VarDecl>(getBeginVarStmt()->getSingleDecl());
   }
 
   VarDecl *getBeginVar() {
+    assert(isIterating());
     return cast<VarDecl>(getBeginVarStmt()->getSingleDecl());
   }
 
   const DeclStmt *getEndVarStmt() const {
-    return cast<DeclStmt>(SubStmts[END]);
+    assert(isIterating());
+    return cast<DeclStmt>(getSubStmt(END));
+  }
+
+  DeclStmt *getEndVarStmt() {
+    assert(isIterating());
+    return cast<DeclStmt>(getSubStmt(END));
+  }
+
+  void setEndVarStmt(DeclStmt *S) {
+    assert(isIterating());
+    getSubStmt(END) = S;
   }
-  DeclStmt *getEndVarStmt() { return cast<DeclStmt>(SubStmts[END]); }
-  void setEndVarStmt(DeclStmt *S) { SubStmts[END] = S; }
 
   const VarDecl *getEndVar() const {
+    assert(isIterating());
     return cast<VarDecl>(getEndVarStmt()->getSingleDecl());
   }
 
   VarDecl *getEndVar() {
+    assert(isIterating());
     return cast<VarDecl>(getEndVarStmt()->getSingleDecl());
   }
 
-  child_range children() {
-    return child_range(SubStmts,
-                       SubStmts + COUNT_CXXIteratingExpansionStmtPattern);
+  // Accessors for destructuring statements.
+  Stmt *getDecompositionDeclStmt() {
+    assert(isDestructuring());
+    return getSubStmt(DECOMP_DECL);
   }
 
-  const_child_range children() const {
-    return const_child_range(SubStmts,
-                             SubStmts + COUNT_CXXIteratingExpansionStmtPattern);
+  const Stmt *getDecompositionDeclStmt() const {
+    assert(isDestructuring());
+    return getSubStmt(DECOMP_DECL);
   }
 
-  static bool classof(const Stmt *T) {
-    return T->getStmtClass() == CXXIteratingExpansionStmtPatternClass;
+  void setDecompositionDeclStmt(Stmt *S) {
+    assert(isDestructuring());
+    getSubStmt(DECOMP_DECL) = S;
   }
-};
-
-/// Represents an unexpanded destructuring expansion statement.
-///
-/// A 'destructuring' expansion statement is any expansion statement that is
-/// not enumerating or iterating (i.e. destructuring is the last thing we try,
-/// and if it doesn't work, the program is ill-formed).
-///
-/// This essentially involves treating the expansion-initializer as the
-/// initializer of a structured-binding declarations, with the number of
-/// bindings and expansion size determined by the usual means (array size,
-/// std::tuple_size, etc.).
-///
-/// Example:
-/// \verbatim
-///   std::array<int, 3> a {1, 2, 3};
-///   template for (auto x : a) {
-///     // ...
-///   }
-/// \endverbatim
-///
-/// Sema wraps the initializer with a CXXDestructuringExpansionSelectExpr, which
-/// selects a binding based on the current expansion index; this is analogous to
-/// how 'CXXExpansionInitListSelectExpr' is used; see the documentation of
-/// 'CXXEnumeratingExpansionStmtPattern' for more details on this.
-class CXXDestructuringExpansionStmtPattern : public CXXExpansionStmtPattern {
-  friend class ASTStmtReader;
-
-public:
-  CXXDestructuringExpansionStmtPattern(EmptyShell Empty);
-  CXXDestructuringExpansionStmtPattern(CXXExpansionStmtDecl *ESD, Stmt *Init,
-                                       DeclStmt *ExpansionVar,
-                                       Stmt *DecompositionDeclStmt,
-                                       SourceLocation LParenLoc,
-                                       SourceLocation ColonLoc,
-                                       SourceLocation RParenLoc);
-
-  Stmt *getDecompositionDeclStmt() { return SubStmts[DECOMP_DECL]; }
-  const Stmt *getDecompositionDeclStmt() const { return SubStmts[DECOMP_DECL]; }
-  void setDecompositionDeclStmt(Stmt *S) { SubStmts[DECOMP_DECL] = S; }
 
   DecompositionDecl *getDecompositionDecl();
   const DecompositionDecl *getDecompositionDecl() const {
-    return const_cast<CXXDestructuringExpansionStmtPattern *>(this)
-        ->getDecompositionDecl();
+    return const_cast<CXXExpansionStmtPattern *>(this)->getDecompositionDecl();
+  }
+
+  // Accessors for dependent statements.
+  Expr *getExpansionInitializer() {
+    assert(isDependent());
+    return cast<Expr>(getSubStmt(EXPANSION_INITIALIZER));
+  }
+
+  const Expr *getExpansionInitializer() const {
+    assert(isDependent());
+    return cast<Expr>(getSubStmt(EXPANSION_INITIALIZER));
+  }
+
+  void setExpansionInitializer(Expr *S) {
+    assert(isDependent());
+    getSubStmt(EXPANSION_INITIALIZER) = S;
   }
 
   child_range children() {
-    return child_range(SubStmts,
-                       SubStmts + COUNT_CXXDestructuringExpansionStmtPattern);
+    return child_range(getTrailingObjects(),
+                       getTrailingObjects() + getNumSubStmts());
   }
 
   const_child_range children() const {
-    return const_child_range(
-        SubStmts, SubStmts + COUNT_CXXDestructuringExpansionStmtPattern);
+    return const_child_range(getTrailingObjects(),
+                             getTrailingObjects() + getNumSubStmts());
   }
 
   static bool classof(const Stmt *T) {
-    return T->getStmtClass() == CXXDestructuringExpansionStmtPatternClass;
+    return T->getStmtClass() == CXXExpansionStmtPatternClass;
+  }
+
+private:
+  template <typename... Args>
+  static CXXExpansionStmtPattern *AllocateAndConstruct(ASTContext &Context,
+                                                       ExpansionStmtKind Kind,
+                                                       Args &&...Arguments);
+
+  static unsigned getNumSubStmts(ExpansionStmtKind Kind);
+  Stmt *getSubStmt(unsigned Idx) const {
+    assert(Idx < getNumSubStmts());
+    return getTrailingObjects()[Idx];
+  }
+
+  Stmt *&getSubStmt(unsigned Idx) {
+    assert(Idx < getNumSubStmts());
+    return getTrailingObjects()[Idx];
   }
 };
 
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 64998c128053a..24a152cfe1de4 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -266,6 +266,7 @@ class TextNodeDumper
   void VisitCoawaitExpr(const CoawaitExpr *Node);
   void VisitCoreturnStmt(const CoreturnStmt *Node);
   void VisitCompoundStmt(const CompoundStmt *Node);
+  void VisitCXXExpansionStmtPattern(const CXXExpansionStmtPattern *Node);
   void
   VisitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *Node);
   void VisitConstantExpr(const ConstantExpr *Node);
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index abbde50c7016b..1df99734f713f 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -59,11 +59,7 @@ def CoroutineBodyStmt : StmtNode<Stmt>;
 def CoreturnStmt : StmtNode<Stmt>;
 
 // C++ expansion statements (P1306)
-def CXXExpansionStmtPattern : StmtNode<Stmt, 1>;
-def CXXEnumeratingExpansionStmtPattern : StmtNode<CXXExpansionStmtPattern>;
-def CXXIteratingExpansionStmtPattern : StmtNode<CXXExpansionStmtPattern>;
-def CXXDestructuringExpansionStmtPattern : StmtNode<CXXExpansionStmtPattern>;
-def CXXDependentExpansionStmtPattern : StmtNode<CXXExpansionStmtPattern>;
+def CXXExpansionStmtPattern : StmtNode<Stmt>;
 def CXXExpansionStmtInstantiation
     : StmtNode<Stmt>; // *Not* derived from CXXExpansionStmtPattern!
 
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 033407dbba2f4..e90fc6bdabca7 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1836,19 +1836,10 @@ enum StmtCode {
 
   STMT_CXX_FOR_RANGE,
 
-  /// A CXXEnumeratedExpansionStmt.
-  STMT_CXX_ENUMERATING_EXPANSION,
+  /// A CXXExpansionPatternStmt.
+  STMT_CXX_EXPANSION_PATTERN,
 
-  /// A CXXIteratingExpansionStmtPattern.
-  STMT_CXX_ITERATING_EXPANSION,
-
-  /// A CXXDestructuringExpansionStmtPattern.
-  STMT_CXX_DESTRUCTURING_EXPANSION,
-
-  /// A CXXDependentExpansionStmtPattern,
-  STMT_CXX_DEPENDENT_EXPANSION,
-
-  /// A CXXExpansionStmtInstantiation.
+  /// A CXXExpansionInstantiationStmt.
   STMT_CXX_EXPANSION_INSTANTIATION,
 
   /// A CXXOperatorCallExpr record.
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 0b65ec4f4cd8c..953646ef4aa08 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -609,14 +609,7 @@ namespace clang {
     ExpectedStmt VisitCXXCatchStmt(CXXCatchStmt *S);
     ExpectedStmt VisitCXXTryStmt(CXXTryStmt *S);
     ExpectedStmt VisitCXXForRangeStmt(CXXForRangeStmt *S);
-    ExpectedStmt VisitCXXEnumeratingExpansionStmtPattern(
-        CXXEnumeratingExpansionStmtPattern *S);
-    ExpectedStmt
-    VisitCXXIteratingExpansionStmtPattern(CXXIteratingExpansionStmtPattern *S);
-    ExpectedStmt VisitCXXDestructuringExpansionStmtPattern(
-        CXXDestructuringExpansionStmtPattern *S);
-    ExpectedStmt
-    VisitCXXDependentExpansionStmtPattern(CXXDependentExpansionStmtPattern *S);
+    ExpectedStmt VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S);
     ExpectedStmt
     VisitCXXExpansionStmtInstantiation(CXXExpansionStmtInstantiation *S);
     // FIXME: MSDependentExistsStmt
@@ -7493,8 +7486,8 @@ ExpectedStmt ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
       ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc);
 }
 
-ExpectedStmt ASTNodeImporter::VisitCXXEnumeratingExpansionStmtPattern(
-    CXXEnumeratingExpansionStmtPattern *S) {
+ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtPattern(
+    CXXExpansionStmtPattern *S) {
   Error Err = Error::success();
   auto ToESD = importChecked(Err, S->getDecl());
   auto ToInit = importChecked(Err, S->getInit());
@@ -7505,64 +7498,49 @@ ExpectedStmt ASTNodeImporter::VisitCXXEnumeratingExpansionStmtPattern(
   if (Err)
     return std::move(Err);
 
-  return new (Importer.getToContext()) CXXEnumeratingExpansionStmtPattern(
-      ToESD, ToInit, ToExpansionVar, ToLParenLoc, ToColonLoc, ToRParenLoc);
-}
-ExpectedStmt ASTNodeImporter::VisitCXXIteratingExpansionStmtPattern(
-    CXXIteratingExpansionStmtPattern *S) {
-  Error Err = Error::success();
-  auto ToESD = importChecked(Err, S->getDecl());
-  auto ToInit = importChecked(Err, S->getInit());
-  auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt());
-  auto ToRange = importChecked(Err, S->getRangeVarStmt());
-  auto ToBegin = importChecked(Err, S->getBeginVarStmt());
-  auto ToEnd = importChecked(Err, S->getEndVarStmt());
-  auto ToLParenLoc = importChecked(Err, S->getLParenLoc());
-  auto ToColonLoc = importChecked(Err, S->getColonLoc());
-  auto ToRParenLoc = importChecked(Err, S->getRParenLoc());
-  if (Err)
-    return std::move(Err);
+  switch (S->getKind()) {
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating:
+    return CXXExpansionStmtPattern::CreateEnumerating(
+        Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToLParenLoc,
+        ToColonLoc, ToRParenLoc);
 
-  return new (Importer.getToContext()) CXXIteratingExpansionStmtPattern(
-      ToESD, ToInit, ToExpansionVar, ToRange, ToBegin, ToEnd, ToLParenLoc,
-      ToColonLoc, ToRParenLoc);
-}
-ExpectedStmt ASTNodeImporter::VisitCXXDestructuringExpansionStmtPattern(
-    CXXDestructuringExpansionStmtPattern *S) {
-  Error Err = Error::success();
-  auto ToESD = importChecked(Err, S->getDecl());
-  auto ToInit = importChecked(Err, S->getInit());
-  auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt());
-  auto ToDecompositionDeclStmt =
-      importChecked(Err, S->getDecompositionDeclStmt());
-  auto ToLParenLoc = importChecked(Err, S->getLParenLoc());
-  auto ToColonLoc = importChecked(Err, S->getColonLoc());
-  auto ToRParenLoc = importChecked(Err, S->getRParenLoc());
-  if (Err)
-    return std::move(Err);
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: {
+    auto ToRange = importChecked(Err, S->getRangeVarStmt());
+    auto ToBegin = importChecked(Err, S->getBeginVarStmt());
+    auto ToEnd = importChecked(Err, S->getEndVarStmt());
+    if (Err)
+      return std::move(Err);
 
-  return new (Importer.getToContext()) CXXDestructuringExpansionStmtPattern(
-      ToESD, ToInit, ToExpansionVar, ToDecompositionDeclStmt, ToLParenLoc,
-      ToColonLoc, ToRParenLoc);
-}
-ExpectedStmt ASTNodeImporter::VisitCXXDependentExpansionStmtPattern(
-    CXXDependentExpansionStmtPattern *S) {
-  Error Err = Error::success();
-  auto ToESD = importChecked(Err, S->getDecl());
-  auto ToInit = importChecked(Err, S->getInit());
-  auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt());
-  auto ToExpansionInitializer =
-      importChecked(Err, S->getExpansionInitializer());
-  auto ToLParenLoc = importChecked(Err, S->getLParenLoc());
-  auto ToColonLoc = importChecked(Err, S->getColonLoc());
-  auto ToRParenLoc = importChecked(Err, S->getRParenLoc());
-  if (Err)
-    return std::move(Err);
+    return CXXExpansionStmtPattern::CreateIterating(
+        Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToRange,
+        ToBegin, ToEnd, ToLParenLoc, ToColonLoc, ToRParenLoc);
+  }
 
-  return new (Importer.getToContext()) CXXDependentExpansionStmtPattern(
-      ToESD, ToInit, ToExpansionVar, ToExpansionInitializer, ToLParenLoc,
-      ToColonLoc, ToRParenLoc);
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: {
+    auto ToDecompositionDeclStmt =
+        importChecked(Err, S->getDecompositionDeclStmt());
+    if (Err)
+      return std::move(Err);
+
+    return CXXExpansionStmtPattern::CreateDestructuring(
+        Importer.getToContext(), ToESD, ToInit, ToExpansionVar,
+        ToDecompositionDeclStmt, ToLParenLoc, ToColonLoc, ToRParenLoc);
+  }
+
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent: {
+    auto ToExpansionInitializer =
+        importChecked(Err, S->getExpansionInitializer());
+    if (Err)
+      return std::move(Err);
+    return CXXExpansionStmtPattern::CreateDependent(
+        Importer.getToContext(), ToESD, ToInit, ToExpansionVar,
+        ToExpansionInitializer, ToLParenLoc, ToColonLoc, ToRParenLoc);
+  }
+  }
+
+  llvm_unreachable("invalid pattern kind");
 }
+
 ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtInstantiation(
     CXXExpansionStmtInstantiation *S) {
   Error Err = Error::success();
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index da4e3be6eaa2f..6f7cddd490e8d 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -127,88 +127,115 @@ CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args)
   llvm::copy(Args.ParamMoves, const_cast<Stmt **>(getParamMoves().data()));
 }
 
-CXXExpansionStmtPattern::CXXExpansionStmtPattern(StmtClass SC, EmptyShell Empty)
-    : Stmt(SC, Empty) {}
+CXXExpansionStmtPattern::CXXExpansionStmtPattern(ExpansionStmtKind PatternKind,
+                                                 EmptyShell Empty)
+    : Stmt(CXXExpansionStmtPatternClass, Empty), PatternKind(PatternKind) {}
 
 CXXExpansionStmtPattern::CXXExpansionStmtPattern(
-    StmtClass SC, CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
-
-    SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation RParenLoc)
-    : Stmt(SC), ParentDecl(ESD), LParenLoc(LParenLoc), ColonLoc(ColonLoc),
-      RParenLoc(RParenLoc) {
+    ExpansionStmtKind PatternKind, CXXExpansionStmtDecl *ESD, Stmt *Init,
+    DeclStmt *ExpansionVar, SourceLocation LParenLoc, SourceLocation ColonLoc,
+    SourceLocation RParenLoc)
+    : Stmt(CXXExpansionStmtPatternClass), PatternKind(PatternKind),
+      LParenLoc(LParenLoc), ColonLoc(ColonLoc), RParenLoc(RParenLoc),
+      ParentDecl(ESD) {
   setInit(Init);
   setExpansionVarStmt(ExpansionVar);
   setBody(nullptr);
 }
 
-CXXEnumeratingExpansionStmtPattern::CXXEnumeratingExpansionStmtPattern(
-    EmptyShell Empty)
-    : CXXExpansionStmtPattern(CXXEnumeratingExpansionStmtPatternClass, Empty) {}
+template <typename... Args>
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::AllocateAndConstruct(
+    ASTContext &Context, ExpansionStmtKind Kind, Args &&...Arguments) {
+  std::size_t Size = totalSizeToAlloc<Stmt *>(getNumSubStmts(Kind));
+  void *Mem = Context.Allocate(Size, alignof(CXXExpansionStmtPattern));
+  return new (Mem)
+      CXXExpansionStmtPattern(Kind, std::forward<Args>(Arguments)...);
+}
 
-CXXEnumeratingExpansionStmtPattern::CXXEnumeratingExpansionStmtPattern(
-    CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
-    SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation RParenLoc)
-    : CXXExpansionStmtPattern(CXXEnumeratingExpansionStmtPatternClass, ESD,
-                              Init, ExpansionVar, LParenLoc, ColonLoc,
-                              RParenLoc) {}
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateDependent(
+    ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+    DeclStmt *ExpansionVar, Expr *ExpansionInitializer,
+    SourceLocation LParenLoc, SourceLocation ColonLoc,
+    SourceLocation RParenLoc) {
+  CXXExpansionStmtPattern *Pattern =
+      AllocateAndConstruct(Context, ExpansionStmtKind::Dependent, ESD, Init,
+                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc);
+  Pattern->setExpansionInitializer(ExpansionInitializer);
+  return Pattern;
+}
 
-SourceLocation CXXExpansionStmtPattern::getBeginLoc() const {
-  return ParentDecl->getLocation();
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateDestructuring(
+    ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+    DeclStmt *ExpansionVar, Stmt *DecompositionDeclStmt,
+    SourceLocation LParenLoc, SourceLocation ColonLoc,
+    SourceLocation RParenLoc) {
+  CXXExpansionStmtPattern *Pattern =
+      AllocateAndConstruct(Context, ExpansionStmtKind::Destructuring, ESD, Init,
+                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc);
+  Pattern->setDecompositionDeclStmt(DecompositionDeclStmt);
+  return Pattern;
 }
 
-VarDecl *CXXExpansionStmtPattern::getExpansionVariable() {
-  Decl *LV = cast<DeclStmt>(getExpansionVarStmt())->getSingleDecl();
-  assert(LV && "No expansion variable in CXXExpansionStmtPattern");
-  return cast<VarDecl>(LV);
+CXXExpansionStmtPattern *
+CXXExpansionStmtPattern::CreateEmpty(ASTContext &Context, EmptyShell Empty,
+                                     ExpansionStmtKind Kind) {
+  return AllocateAndConstruct(Context, Kind, Empty);
 }
 
-CXXIteratingExpansionStmtPattern::CXXIteratingExpansionStmtPattern(
-    EmptyShell Empty)
-    : CXXExpansionStmtPattern(CXXIteratingExpansionStmtPatternClass, Empty) {}
-
-CXXIteratingExpansionStmtPattern::CXXIteratingExpansionStmtPattern(
-    CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
-    DeclStmt *Range, DeclStmt *Begin, DeclStmt *End, SourceLocation LParenLoc,
-    SourceLocation ColonLoc, SourceLocation RParenLoc)
-    : CXXExpansionStmtPattern(CXXIteratingExpansionStmtPatternClass, ESD, Init,
-                              ExpansionVar, LParenLoc, ColonLoc, RParenLoc) {
-  setRangeVarStmt(Range);
-  setBeginVarStmt(Begin);
-  setEndVarStmt(End);
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateEnumerating(
+    ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+    DeclStmt *ExpansionVar, SourceLocation LParenLoc, SourceLocation ColonLoc,
+    SourceLocation RParenLoc) {
+  return AllocateAndConstruct(Context, ExpansionStmtKind::Enumerating, ESD,
+                              Init, ExpansionVar, LParenLoc, ColonLoc,
+                              RParenLoc);
 }
 
-CXXDestructuringExpansionStmtPattern::CXXDestructuringExpansionStmtPattern(
-    EmptyShell Empty)
-    : CXXExpansionStmtPattern(CXXDestructuringExpansionStmtPatternClass,
-                              Empty) {}
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateIterating(
+    ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+    DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin, DeclStmt *End,
+    SourceLocation LParenLoc, SourceLocation ColonLoc,
+    SourceLocation RParenLoc) {
+  CXXExpansionStmtPattern *Pattern =
+      AllocateAndConstruct(Context, ExpansionStmtKind::Iterating, ESD, Init,
+                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc);
+  Pattern->setRangeVarStmt(Range);
+  Pattern->setBeginVarStmt(Begin);
+  Pattern->setEndVarStmt(End);
+  return Pattern;
+}
 
-CXXDestructuringExpansionStmtPattern::CXXDestructuringExpansionStmtPattern(
-    CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
-    Stmt *DecompositionDeclStmt, SourceLocation LParenLoc,
-    SourceLocation ColonLoc, SourceLocation RParenLoc)
-    : CXXExpansionStmtPattern(CXXDestructuringExpansionStmtPatternClass, ESD,
-                              Init, ExpansionVar, LParenLoc, ColonLoc,
-                              RParenLoc) {
-  setDecompositionDeclStmt(DecompositionDeclStmt);
+SourceLocation CXXExpansionStmtPattern::getBeginLoc() const {
+  return ParentDecl->getLocation();
 }
 
 DecompositionDecl *
-CXXDestructuringExpansionStmtPattern::getDecompositionDecl() {
+CXXExpansionStmtPattern::getDecompositionDecl() {
+  assert(isDestructuring());
   return cast<DecompositionDecl>(
       cast<DeclStmt>(getDecompositionDeclStmt())->getSingleDecl());
 }
 
-CXXDependentExpansionStmtPattern::CXXDependentExpansionStmtPattern(
-    EmptyShell Empty)
-    : CXXExpansionStmtPattern(CXXDependentExpansionStmtPatternClass, Empty) {}
-
-CXXDependentExpansionStmtPattern::CXXDependentExpansionStmtPattern(
-    CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar,
-    Expr *ExpansionInitializer, SourceLocation LParenLoc,
-    SourceLocation ColonLoc, SourceLocation RParenLoc)
-    : CXXExpansionStmtPattern(CXXDependentExpansionStmtPatternClass, ESD, Init,
-                              ExpansionVar, LParenLoc, ColonLoc, RParenLoc) {
-  setExpansionInitializer(ExpansionInitializer);
+VarDecl *CXXExpansionStmtPattern::getExpansionVariable() {
+  Decl *LV = cast<DeclStmt>(getExpansionVarStmt())->getSingleDecl();
+  assert(LV && "No expansion variable in CXXExpansionStmtPattern");
+  return cast<VarDecl>(LV);
+}
+
+unsigned
+CXXExpansionStmtPattern::getNumSubStmts(ExpansionStmtKind PatternKind) {
+  switch (PatternKind) {
+  case ExpansionStmtKind::Enumerating:
+    return COUNT_Enumerating;
+  case ExpansionStmtKind::Iterating:
+    return COUNT_Iterating;
+  case ExpansionStmtKind::Destructuring:
+    return COUNT_Destructuring;
+  case ExpansionStmtKind::Dependent:
+    return COUNT_Dependent;
+  }
+
+  llvm_unreachable("invalid pattern kind");
 }
 
 CXXExpansionStmtInstantiation::CXXExpansionStmtInstantiation(
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 165bbd046f5f4..d2f8d62582152 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -160,8 +160,6 @@ namespace {
     }
 
     void VisitCXXNamedCastExpr(CXXNamedCastExpr *Node);
-    void VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node,
-                                      Expr *Initializer = nullptr);
 
 #define ABSTRACT_STMT(CLASS)
 #define STMT(CLASS, PARENT) \
@@ -450,8 +448,7 @@ void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt *Node) {
   PrintControlledStmt(Node->getBody());
 }
 
-void StmtPrinter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node,
-                                               Expr *Initializer) {
+void StmtPrinter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node) {
   OS << "template for (";
   if (Node->getInit())
     PrintInitStmt(Node->getInit(), 14);
@@ -459,30 +456,16 @@ void StmtPrinter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node,
   SubPolicy.SuppressInitializers = true;
   Node->getExpansionVariable()->print(OS, SubPolicy, IndentLevel);
   OS << " : ";
-  PrintExpr(Initializer ? Initializer
-                        : Node->getExpansionVariable()->getInit());
-  OS << ")";
-  PrintControlledStmt(Node->getBody());
-}
 
-void StmtPrinter::VisitCXXEnumeratingExpansionStmtPattern(
-    CXXEnumeratingExpansionStmtPattern *Node) {
-  VisitCXXExpansionStmtPattern(Node);
-}
-
-void StmtPrinter::VisitCXXIteratingExpansionStmtPattern(
-    CXXIteratingExpansionStmtPattern *Node) {
-  VisitCXXExpansionStmtPattern(Node, Node->getRangeVar()->getInit());
-}
-
-void StmtPrinter::VisitCXXDestructuringExpansionStmtPattern(
-    CXXDestructuringExpansionStmtPattern *Node) {
-  VisitCXXExpansionStmtPattern(Node);
-}
+  if (Node->isIterating())
+    PrintExpr(Node->getRangeVar()->getInit());
+  else if (Node->isDependent())
+    PrintExpr(Node->getExpansionInitializer());
+  else
+    PrintExpr(Node->getExpansionVariable()->getInit());
 
-void StmtPrinter::VisitCXXDependentExpansionStmtPattern(
-    CXXDependentExpansionStmtPattern *Node) {
-  VisitCXXExpansionStmtPattern(Node, Node->getExpansionInitializer());
+  OS << ")";
+  PrintControlledStmt(Node->getBody());
 }
 
 void StmtPrinter::VisitCXXExpansionStmtInstantiation(
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 08759bbaa2ed3..767479be115f5 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -369,26 +369,6 @@ void StmtProfiler::VisitCXXExpansionStmtPattern(
   VisitStmt(S);
 }
 
-void StmtProfiler::VisitCXXEnumeratingExpansionStmtPattern(
-    const CXXEnumeratingExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-}
-
-void StmtProfiler::VisitCXXIteratingExpansionStmtPattern(
-    const CXXIteratingExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-}
-
-void StmtProfiler::VisitCXXDestructuringExpansionStmtPattern(
-    const CXXDestructuringExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-}
-
-void StmtProfiler::VisitCXXDependentExpansionStmtPattern(
-    const CXXDependentExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-}
-
 void StmtProfiler::VisitCXXExpansionStmtInstantiation(
     const CXXExpansionStmtInstantiation *S) {
   VisitStmt(S);
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index e20cb63ee54b8..453bcaa5de40f 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1499,6 +1499,24 @@ void clang::TextNodeDumper::VisitCoreturnStmt(const CoreturnStmt *Node) {
     OS << " implicit";
 }
 
+void TextNodeDumper::VisitCXXExpansionStmtPattern(
+    const CXXExpansionStmtPattern *Node) {
+  switch (Node->getKind()) {
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating:
+    OS << " enumerating";
+    break;
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
+    OS << " iterating";
+    break;
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
+    OS << " destructuring";
+    break;
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent:
+    OS << " dependent";
+    break;
+  }
+}
+
 void TextNodeDumper::VisitCXXExpansionStmtInstantiation(
     const CXXExpansionStmtInstantiation *Node) {
   if (Node->shouldApplyLifetimeExtensionToSharedStmts())
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 40c92035008ae..6f02ccd99c0e4 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -204,10 +204,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
   case Stmt::CXXForRangeStmtClass:
     EmitCXXForRangeStmt(cast<CXXForRangeStmt>(*S), Attrs);
     break;
-  case Stmt::CXXEnumeratingExpansionStmtPatternClass:
-  case Stmt::CXXIteratingExpansionStmtPatternClass:
-  case Stmt::CXXDestructuringExpansionStmtPatternClass:
-  case Stmt::CXXDependentExpansionStmtPatternClass:
+  case Stmt::CXXExpansionStmtPatternClass:
     llvm_unreachable("unexpanded expansion statements should not be emitted");
   case Stmt::CXXExpansionStmtInstantiationClass:
     llvm_unreachable("Todo");
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 5839785dbaab2..9c072fa07d1c4 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1350,7 +1350,6 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::DependentScopeDeclRefExprClass:
   case Expr::CXXFoldExprClass:
   case Expr::RecoveryExprClass:
-  case Expr::CXXDependentExpansionStmtPatternClass:
     return CT_Dependent;
 
   case Expr::AsTypeExprClass:
@@ -1541,12 +1540,15 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Stmt::SEHTryStmtClass:
   case Stmt::SwitchStmtClass:
   case Stmt::WhileStmtClass:
-  case Stmt::CXXEnumeratingExpansionStmtPatternClass:
-  case Stmt::CXXIteratingExpansionStmtPatternClass:
-  case Stmt::CXXDestructuringExpansionStmtPatternClass:
   case Stmt::CXXExpansionStmtInstantiationClass:
     return canSubStmtsThrow(*this, S);
 
+  case Stmt::CXXExpansionStmtPatternClass:
+    if (auto *Pattern = cast<CXXExpansionStmtPattern>(S);
+        Pattern->isDependent())
+      return CT_Dependent;
+    return canSubStmtsThrow(*this, S);
+
   case Stmt::DeclStmtClass: {
     CanThrowResult CT = CT_Cannot;
     for (const Decl *D : cast<DeclStmt>(S)->decls()) {
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index f3ea8c99666eb..9b91f56fa3ff6 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1732,13 +1732,13 @@ void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
 
 void ASTStmtReader::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
   VisitStmt(S);
+  Record.skipInts(1); // Skip kind.
   S->LParenLoc = readSourceLocation();
   S->ColonLoc = readSourceLocation();
   S->RParenLoc = readSourceLocation();
   S->ParentDecl = cast<CXXExpansionStmtDecl>(Record.readDeclRef());
-  S->setInit(Record.readSubStmt());
-  S->setExpansionVarStmt(Record.readSubStmt());
-  S->setBody(Record.readSubStmt());
+  for (Stmt *&SubStmt : S->children())
+    SubStmt = Record.readSubStmt();
 }
 
 void ASTStmtReader::VisitCXXExpansionStmtInstantiation(
@@ -1752,31 +1752,6 @@ void ASTStmtReader::VisitCXXExpansionStmtInstantiation(
   S->setShouldApplyLifetimeExtensionToSharedStmts(Record.readBool());
 }
 
-void ASTStmtReader::VisitCXXEnumeratingExpansionStmtPattern(
-    CXXEnumeratingExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-}
-
-void ASTStmtReader::VisitCXXIteratingExpansionStmtPattern(
-    CXXIteratingExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-  S->setRangeVarStmt(cast<DeclStmt>(Record.readSubStmt()));
-  S->setBeginVarStmt(cast<DeclStmt>(Record.readSubStmt()));
-  S->setEndVarStmt(cast<DeclStmt>(Record.readSubStmt()));
-}
-
-void ASTStmtReader::VisitCXXDestructuringExpansionStmtPattern(
-    CXXDestructuringExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-  S->setDecompositionDeclStmt(Record.readSubStmt());
-}
-
-void ASTStmtReader::VisitCXXDependentExpansionStmtPattern(
-    CXXDependentExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-  S->setExpansionInitializer(Record.readSubExpr());
-}
-
 void ASTStmtReader::VisitCXXExpansionInitListSelectExpr(
     CXXExpansionInitListSelectExpr *E) {
   VisitExpr(E);
@@ -3627,20 +3602,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
              /*numHandlers=*/Record[ASTStmtReader::NumStmtFields]);
       break;
 
-    case STMT_CXX_ENUMERATING_EXPANSION:
-      S = new (Context) CXXEnumeratingExpansionStmtPattern(Empty);
-      break;
-
-    case STMT_CXX_ITERATING_EXPANSION:
-      S = new (Context) CXXIteratingExpansionStmtPattern(Empty);
-      break;
-
-    case STMT_CXX_DESTRUCTURING_EXPANSION:
-      S = new (Context) CXXDestructuringExpansionStmtPattern(Empty);
-      break;
-
-    case STMT_CXX_DEPENDENT_EXPANSION:
-      S = new (Context) CXXDependentExpansionStmtPattern(Empty);
+    case STMT_CXX_EXPANSION_PATTERN:
+      S = CXXExpansionStmtPattern::CreateEmpty(
+          Context, Empty,
+          static_cast<CXXExpansionStmtPattern::ExpansionStmtKind>(
+              Record[ASTStmtReader::NumStmtFields]));
       break;
 
     case STMT_CXX_EXPANSION_INSTANTIATION:
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 8319a59bc951f..8b36dea086427 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1706,13 +1706,14 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
 
 void ASTStmtWriter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
   VisitStmt(S);
+  Record.push_back(static_cast<unsigned>(S->getKind()));
   Record.AddSourceLocation(S->getLParenLoc());
   Record.AddSourceLocation(S->getColonLoc());
   Record.AddSourceLocation(S->getRParenLoc());
   Record.AddDeclRef(S->getDecl());
-  Record.AddStmt(S->getInit());
-  Record.AddStmt(S->getExpansionVarStmt());
-  Record.AddStmt(S->getBody());
+  for (Stmt* SubStmt : S->children())
+    Record.AddStmt(SubStmt);
+  Code = serialization::STMT_CXX_EXPANSION_PATTERN;
 }
 
 void ASTStmtWriter::VisitCXXExpansionStmtInstantiation(
@@ -1728,35 +1729,6 @@ void ASTStmtWriter::VisitCXXExpansionStmtInstantiation(
   Code = serialization::STMT_CXX_EXPANSION_INSTANTIATION;
 }
 
-void ASTStmtWriter::VisitCXXEnumeratingExpansionStmtPattern(
-    CXXEnumeratingExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-  Code = serialization::STMT_CXX_ENUMERATING_EXPANSION;
-}
-
-void ASTStmtWriter::VisitCXXIteratingExpansionStmtPattern(
-    CXXIteratingExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-  Record.AddStmt(S->getRangeVarStmt());
-  Record.AddStmt(S->getBeginVarStmt());
-  Record.AddStmt(S->getEndVarStmt());
-  Code = serialization::STMT_CXX_ITERATING_EXPANSION;
-}
-
-void ASTStmtWriter::VisitCXXDestructuringExpansionStmtPattern(
-    CXXDestructuringExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-  Record.AddStmt(S->getDecompositionDeclStmt());
-  Code = serialization::STMT_CXX_DESTRUCTURING_EXPANSION;
-}
-
-void ASTStmtWriter::VisitCXXDependentExpansionStmtPattern(
-    CXXDependentExpansionStmtPattern *S) {
-  VisitCXXExpansionStmtPattern(S);
-  Record.AddStmt(S->getExpansionInitializer());
-  Code = serialization::STMT_CXX_DEPENDENT_EXPANSION;
-}
-
 void ASTStmtWriter::VisitCXXExpansionInitListSelectExpr(
     CXXExpansionInitListSelectExpr *E) {
   VisitExpr(E);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 53f85c22dd020..3e6f3a7b9090e 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1752,10 +1752,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::SEHExceptStmtClass:
     case Stmt::SEHLeaveStmtClass:
     case Stmt::SEHFinallyStmtClass:
-    case Stmt::CXXEnumeratingExpansionStmtPatternClass:
-    case Stmt::CXXIteratingExpansionStmtPatternClass:
-    case Stmt::CXXDestructuringExpansionStmtPatternClass:
-    case Stmt::CXXDependentExpansionStmtPatternClass:
+    case Stmt::CXXExpansionStmtPatternClass:
     case Stmt::CXXExpansionStmtInstantiationClass:
     case Stmt::CXXExpansionInitListSelectExprClass:
     case Stmt::CXXDestructuringExpansionSelectExprClass:
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index ecd1f86c3ddb6..16e02f5a79e01 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -290,10 +290,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
 
   case Stmt::CoroutineBodyStmtClass:
   case Stmt::CoreturnStmtClass:
-  case Stmt::CXXEnumeratingExpansionStmtPatternClass:
-  case Stmt::CXXIteratingExpansionStmtPatternClass:
-  case Stmt::CXXDestructuringExpansionStmtPatternClass:
-  case Stmt::CXXDependentExpansionStmtPatternClass:
+  case Stmt::CXXExpansionStmtPatternClass:
   case Stmt::CXXExpansionStmtInstantiationClass:
     K = CXCursor_UnexposedStmt;
     break;

>From 66a2c59b9b4c4b94bfa31eba2b3bbd603c053bd0 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 3 Dec 2025 20:16:07 +0100
Subject: [PATCH 8/9] Stub out TreeTransform properly

---
 clang/lib/Sema/TreeTransform.h | 33 ++-------------------------------
 1 file changed, 2 insertions(+), 31 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index f181d0abb5dfd..39afc5692b42f 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9293,37 +9293,8 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
 }
 
 template <typename Derived>
-StmtResult TreeTransform<Derived>::TransformCXXEnumeratingExpansionStmtPattern(
-    CXXEnumeratingExpansionStmtPattern *S) {
-  llvm_unreachable("TOOD");
-}
-
-template <typename Derived>
-StmtResult TreeTransform<Derived>::TransformCXXIteratingExpansionStmtPattern(
-    CXXIteratingExpansionStmtPattern *S) {
-  llvm_unreachable("TOOD");
-}
-
-template <typename Derived>
-StmtResult TreeTransform<Derived>::TransformCXXDependentExpansionStmtPattern(
-    CXXDependentExpansionStmtPattern *S) {
-  llvm_unreachable("TOOD");
-}
-
-template <typename Derived>
-StmtResult
-TreeTransform<Derived>::TransformCXXDestructuringExpansionStmtPattern(
-    CXXDestructuringExpansionStmtPattern *) {
-  // The only time we instantiate an expansion statement is if its expansion
-  // size is dependent (otherwise, we only instantiate the expansions and
-  // leave the underlying CXXExpansionStmtPattern as-is). Since destructuring
-  // expansion statements never have a dependent size, we should never get here.
-  llvm_unreachable("Should never be instantiated");
-}
-
-template <typename Derived>
-ExprResult TreeTransform<Derived>::TransformCXXExpansionInitListExpr(
-    CXXExpansionInitListExpr *E) {
+StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
+    CXXExpansionStmtPattern *S) {
   llvm_unreachable("TOOD");
 }
 

>From 8432c3da1bfc8e38d02065f64497ac3a2991be39 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 3 Dec 2025 22:10:24 +0100
Subject: [PATCH 9/9] Only use a single CXXExpansionSelectExpr

---
 clang/include/clang/AST/DeclTemplate.h        | 19 ++----
 clang/include/clang/AST/ExprCXX.h             | 65 ++-----------------
 clang/include/clang/AST/RecursiveASTVisitor.h |  3 +-
 clang/include/clang/AST/StmtCXX.h             | 11 ++--
 clang/include/clang/AST/TextNodeDumper.h      |  2 -
 clang/include/clang/Basic/StmtNodes.td        |  3 +-
 .../include/clang/Serialization/ASTBitCodes.h | 23 ++++---
 clang/lib/AST/ASTImporter.cpp                 | 23 ++-----
 clang/lib/AST/Expr.cpp                        |  3 +-
 clang/lib/AST/ExprCXX.cpp                     | 20 ++----
 clang/lib/AST/ExprClassification.cpp          |  3 +-
 clang/lib/AST/ExprConstant.cpp                |  3 +-
 clang/lib/AST/ItaniumMangle.cpp               |  3 +-
 clang/lib/AST/StmtPrinter.cpp                 | 11 ++--
 clang/lib/AST/StmtProfile.cpp                 | 10 +--
 clang/lib/AST/TextNodeDumper.cpp              |  5 --
 clang/lib/Sema/SemaExceptionSpec.cpp          |  3 +-
 clang/lib/Sema/TreeTransform.h                | 10 +--
 clang/lib/Serialization/ASTReaderStmt.cpp     | 19 ++----
 clang/lib/Serialization/ASTWriterStmt.cpp     | 14 +---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |  3 +-
 clang/tools/libclang/CXCursor.cpp             |  3 +-
 22 files changed, 59 insertions(+), 200 deletions(-)

diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index d0fbbabe94aa2..d30c197853ce5 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3375,16 +3375,8 @@ class TemplateParamObjectDecl : public ValueDecl,
 /// 'CXXExpansionStmtInstantiation' is. The latter is also what's used for
 /// codegen and constant evaluation.
 ///
-/// There are three kinds of expansion statements; they correspond to three
-/// derived classes of 'CXXExpansionStmtPattern'. There is also a fourth derived
-/// class that is used if we don't know what kind of expansion statement we're
-/// dealing with (because the thing we're expanding is dependent). See the
-/// comment on those classes for more information about how they work:
-///
-///   1. CXXEnumeratingExpansionStmtPattern
-///   2. CXXIteratingExpansionStmtPattern
-///   3. CXXDestructuringExpansionStmtPattern
-///   4. CXXDependentExpansionStmtPattern
+/// There are different kinds of expansion statements; see the comment on
+/// 'CXXExpansionStmtPattern' for more information.
 ///
 /// As an example, if the user writes the following expansion statement:
 /// \verbatim
@@ -3394,10 +3386,9 @@ class TemplateParamObjectDecl : public ValueDecl,
 ///   }
 /// \endverbatim
 ///
-/// The 'CXXExpansionStmtPattern' of this particular 'CXXExpansionStmtDecl' is a
-/// 'CXXDestructuringExpansionStmtPattern', which stores, amongst other things,
-/// the declaration of the variable 'x' as well as the expansion-initializer
-/// 'a'.
+/// The 'CXXExpansionStmtPattern' of this particular 'CXXExpansionStmtDecl'
+/// stores, amongst other things, the declaration of the variable 'x' as well
+/// as the expansion-initializer 'a'.
 ///
 /// After expansion, we end up with a 'CXXExpansionStmtInstantiation' that
 /// is *equivalent* to the AST shown below. Note that only the inner '{}' (i.e.
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 6bf2d52399285..083576c5379bf 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5499,20 +5499,19 @@ class BuiltinBitCastExpr final
   }
 };
 
-/// Helper that selects an expression from an expansion init list depending
+/// Helper that selects an expression from an InitListExpr depending
 /// on the current expansion index.
 ///
-/// \see CXXEnumeratingExpansionStmtPattern
-class CXXExpansionInitListSelectExpr : public Expr {
+/// \see CXXExpansionStmtPattern
+class CXXExpansionSelectExpr : public Expr {
   friend class ASTStmtReader;
 
   enum SubExpr { RANGE, INDEX, COUNT };
   Expr *SubExprs[COUNT];
 
 public:
-  CXXExpansionInitListSelectExpr(EmptyShell Empty);
-  CXXExpansionInitListSelectExpr(const ASTContext &C,
-                                 InitListExpr *Range, Expr *Idx);
+  CXXExpansionSelectExpr(EmptyShell Empty);
+  CXXExpansionSelectExpr(const ASTContext &C, InitListExpr *Range, Expr *Idx);
 
   InitListExpr *getRangeExpr() {
     return cast<InitListExpr>(SubExprs[RANGE]);
@@ -5543,61 +5542,9 @@ class CXXExpansionInitListSelectExpr : public Expr {
   }
 
   static bool classof(const Stmt *T) {
-    return T->getStmtClass() == CXXExpansionInitListSelectExprClass;
+    return T->getStmtClass() == CXXExpansionSelectExprClass;
   }
 };
-
-/// This class serves the same purpose as CXXExpansionInitListSelectExpr, but
-/// for destructuring expansion statements; that is, instead of selecting among
-/// a list of expressions, it selects from a list of 'BindingDecl's.
-///
-/// \see CXXEnumeratingExpansionStmtPattern
-/// \see CXXDestructuringExpansionStmtPattern
-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 b145fa9f9cdf0..5502d68f99ad3 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3127,8 +3127,7 @@ DEF_TRAVERSE_STMT(RequiresExpr, {
 
 DEF_TRAVERSE_STMT(CXXExpansionStmtPattern, {})
 DEF_TRAVERSE_STMT(CXXExpansionStmtInstantiation, {})
-DEF_TRAVERSE_STMT(CXXExpansionInitListSelectExpr, {})
-DEF_TRAVERSE_STMT(CXXDestructuringExpansionSelectExpr, {})
+DEF_TRAVERSE_STMT(CXXExpansionSelectExpr, {})
 
 // These literals (all of them) do not need any action.
 DEF_TRAVERSE_STMT(IntegerLiteral, {})
diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index fcc30c3b1d421..1caae321df54c 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -554,14 +554,14 @@ class CoreturnStmt : public Stmt {
 /// Here, the '{ 1, 2, 3 }' is parsed as an 'InitListExpr'. This node
 /// handles storing (and pack-expanding) the individual expressions.
 ///
-/// Sema then wraps this with a 'CXXExpansionInitListSelectExpr', which also
+/// Sema then wraps this with a 'CXXExpansionSelectExpr', which also
 /// contains a reference to an integral NTTP that is used as the expansion
 /// index; this index is either dependent (if the expansion-size is dependent),
 /// or set to a value of I in the I-th expansion during the expansion process.
 ///
-/// The actual expansion is done by 'BuildCXXExpansionInitListSelectExpr()': for
+/// The actual expansion is done by 'BuildCXXExpansionSelectExpr()': for
 /// example, during the 2nd expansion of '{ a, b, c }', I is equal to 1, and
-/// BuildCXXExpansionInitListSelectExpr(), when called via TreeTransform,
+/// BuildCXXExpansionSelectExpr(), when called via TreeTransform,
 /// 'instantiates' the expression '{ a, b, c }' to just 'b'.
 ///
 /// 2. Represents an unexpanded iterating expansion statement.
@@ -601,9 +601,8 @@ class CoreturnStmt : public Stmt {
 ///   }
 /// \endverbatim
 ///
-/// Sema wraps the initializer with a CXXDestructuringExpansionSelectExpr, which
-/// selects a binding based on the current expansion index; this is analogous to
-/// how 'CXXExpansionInitListSelectExpr' is used.
+/// Sema wraps the initializer with a CXXExpansionSelectExpr, which selects a
+/// binding based on the current expansion index.
 ///
 /// 4. Represents an expansion statement whose expansion-initializer is
 /// type-dependent.
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 24a152cfe1de4..499dc74d51099 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -313,8 +313,6 @@ class TextNodeDumper
   void VisitSizeOfPackExpr(const SizeOfPackExpr *Node);
   void
   VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *Node);
-  void VisitCXXDestructuringExpansionSelectExpr(
-      const CXXDestructuringExpansionSelectExpr *Node);
   void VisitObjCAtCatchStmt(const ObjCAtCatchStmt *Node);
   void VisitObjCEncodeExpr(const ObjCEncodeExpr *Node);
   void VisitObjCMessageExpr(const ObjCMessageExpr *Node);
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index 1df99734f713f..d51d4c6d23f7d 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -183,8 +183,7 @@ def ConceptSpecializationExpr : StmtNode<Expr>;
 def RequiresExpr : StmtNode<Expr>;
 
 // C++26 Expansion statement support expressions
-def CXXExpansionInitListSelectExpr : StmtNode<Expr>;
-def CXXDestructuringExpansionSelectExpr : StmtNode<Expr>;
+def CXXExpansionSelectExpr : StmtNode<Expr>;
 
 // Obj-C Expressions.
 def ObjCStringLiteral : StmtNode<Expr>;
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index e90fc6bdabca7..ba5713d24a048 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1923,18 +1923,17 @@ enum StmtCode {
   EXPR_TYPE_TRAIT,                  // TypeTraitExpr
   EXPR_ARRAY_TYPE_TRAIT,            // ArrayTypeTraitIntExpr
 
-  EXPR_PACK_EXPANSION,                     // PackExpansionExpr
-  EXPR_PACK_INDEXING,                      // PackIndexingExpr
-  EXPR_SIZEOF_PACK,                        // SizeOfPackExpr
-  EXPR_SUBST_NON_TYPE_TEMPLATE_PARM,       // SubstNonTypeTemplateParmExpr
-  EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,  // SubstNonTypeTemplateParmPackExpr
-  EXPR_FUNCTION_PARM_PACK,                 // FunctionParmPackExpr
-  EXPR_MATERIALIZE_TEMPORARY,              // MaterializeTemporaryExpr
-  EXPR_CXX_FOLD,                           // CXXFoldExpr
-  EXPR_CONCEPT_SPECIALIZATION,             // ConceptSpecializationExpr
-  EXPR_REQUIRES,                           // RequiresExpr
-  EXPR_CXX_EXPANSION_INIT_LIST_SELECT,     // CXXExpansionInitListSelectExpr
-  EXPR_CXX_DESTRUCTURING_EXPANSION_SELECT, // CXXDestructuringExpansionSelectExpr
+  EXPR_PACK_EXPANSION,                    // PackExpansionExpr
+  EXPR_PACK_INDEXING,                     // PackIndexingExpr
+  EXPR_SIZEOF_PACK,                       // SizeOfPackExpr
+  EXPR_SUBST_NON_TYPE_TEMPLATE_PARM,      // SubstNonTypeTemplateParmExpr
+  EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr
+  EXPR_FUNCTION_PARM_PACK,                // FunctionParmPackExpr
+  EXPR_MATERIALIZE_TEMPORARY,             // MaterializeTemporaryExpr
+  EXPR_CXX_FOLD,                          // CXXFoldExpr
+  EXPR_CONCEPT_SPECIALIZATION,            // ConceptSpecializationExpr
+  EXPR_REQUIRES,                          // RequiresExpr
+  EXPR_CXX_EXPANSION_SELECT,              // CXXExpansionSelectExpr
 
   // CUDA
   EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 953646ef4aa08..148a5b514b8c8 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -704,10 +704,7 @@ namespace clang {
     VisitSubstNonTypeTemplateParmPackExpr(SubstNonTypeTemplateParmPackExpr *E);
     ExpectedStmt VisitPseudoObjectExpr(PseudoObjectExpr *E);
     ExpectedStmt VisitCXXParenListInitExpr(CXXParenListInitExpr *E);
-    ExpectedStmt
-    VisitCXXExpansionInitListSelectExpr(CXXExpansionInitListSelectExpr *E);
-    ExpectedStmt VisitCXXDestructuringExpansionSelectExpr(
-        CXXDestructuringExpansionSelectExpr *E);
+    ExpectedStmt VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *E);
 
     // Helper for chaining together multiple imports. If an error is detected,
     // subsequent imports will return default constructed nodes, so that failure
@@ -9457,8 +9454,8 @@ ASTNodeImporter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
                                       ToInitLoc, ToBeginLoc, ToEndLoc);
 }
 
-ExpectedStmt ASTNodeImporter::VisitCXXExpansionInitListSelectExpr(
-    CXXExpansionInitListSelectExpr *E) {
+ExpectedStmt ASTNodeImporter::VisitCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *E) {
   Error Err = Error::success();
   auto ToRange = importChecked(Err, E->getRangeExpr());
   auto ToIndex = importChecked(Err, E->getIndexExpr());
@@ -9466,19 +9463,7 @@ ExpectedStmt ASTNodeImporter::VisitCXXExpansionInitListSelectExpr(
     return std::move(Err);
 
   return new (Importer.getToContext())
-      CXXExpansionInitListSelectExpr(Importer.getToContext(), ToRange, ToIndex);
-}
-
-ExpectedStmt ASTNodeImporter::VisitCXXDestructuringExpansionSelectExpr(
-    CXXDestructuringExpansionSelectExpr *E) {
-  Error Err = Error::success();
-  auto ToDecompositionDecl = importChecked(Err, E->getDecompositionDecl());
-  auto ToIndex = importChecked(Err, E->getIndexExpr());
-  if (Err)
-    return std::move(Err);
-
-  return new (Importer.getToContext()) CXXDestructuringExpansionSelectExpr(
-      Importer.getToContext(), ToDecompositionDecl, ToIndex);
+      CXXExpansionSelectExpr(Importer.getToContext(), ToRange, ToIndex);
 }
 
 Error ASTNodeImporter::ImportOverriddenMethods(CXXMethodDecl *ToMethod,
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index af2d264623f89..2911b0da96bbf 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3688,8 +3688,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
   case FunctionParmPackExprClass:
   case RecoveryExprClass:
   case CXXFoldExprClass:
-  case CXXExpansionInitListSelectExprClass:
-  case CXXDestructuringExpansionSelectExprClass:
+  case CXXExpansionSelectExprClass:
     // Make a conservative assumption for dependent nodes.
     return IncludePossibleEffects;
 
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 8975d9af87ce1..8898547e23c0f 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -2021,26 +2021,14 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
   setDependence(computeDependence(this));
 }
 
-CXXExpansionInitListSelectExpr::CXXExpansionInitListSelectExpr(EmptyShell Empty)
-    : Expr(CXXExpansionInitListSelectExprClass, Empty) {}
+CXXExpansionSelectExpr::CXXExpansionSelectExpr(EmptyShell Empty)
+    : Expr(CXXExpansionSelectExprClass, Empty) {}
 
-CXXExpansionInitListSelectExpr::CXXExpansionInitListSelectExpr(
+CXXExpansionSelectExpr::CXXExpansionSelectExpr(
     const ASTContext &C, InitListExpr *Range, Expr *Idx)
-    : Expr(CXXExpansionInitListSelectExprClass, C.DependentTy, VK_PRValue,
+    : Expr(CXXExpansionSelectExprClass, C.DependentTy, VK_PRValue,
            OK_Ordinary) {
   setDependence(ExprDependence::TypeValueInstantiation);
   SubExprs[RANGE] = Range;
   SubExprs[INDEX] = Idx;
 }
-
-CXXDestructuringExpansionSelectExpr::CXXDestructuringExpansionSelectExpr(
-    EmptyShell Empty)
-    : Expr(CXXDestructuringExpansionSelectExprClass, Empty) {}
-
-CXXDestructuringExpansionSelectExpr::CXXDestructuringExpansionSelectExpr(
-    const ASTContext &C, DecompositionDecl *Decomposition, Expr *Index)
-    : Expr(CXXDestructuringExpansionSelectExprClass, C.DependentTy, VK_PRValue,
-           OK_Ordinary),
-      Decomposition(Decomposition), Index(Index) {
-  setDependence(ExprDependence::TypeValueInstantiation);
-}
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index 1b139ac0954b8..3a1ed33e5e5f8 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -216,8 +216,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
   case Expr::SourceLocExprClass:
   case Expr::ConceptSpecializationExprClass:
   case Expr::RequiresExprClass:
-  case Expr::CXXExpansionInitListSelectExprClass:
-  case Expr::CXXDestructuringExpansionSelectExprClass:
+  case Expr::CXXExpansionSelectExprClass:
     return Cl::CL_PRValue;
 
   case Expr::EmbedExprClass:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 0bd56bb0a76e2..7ab4f3ce9ffaf 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -20276,8 +20276,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
   case Expr::SYCLUniqueStableNameExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::HLSLOutArgExprClass:
-  case Expr::CXXExpansionInitListSelectExprClass:
-  case Expr::CXXDestructuringExpansionSelectExprClass:
+  case Expr::CXXExpansionSelectExprClass:
     return ICEDiag(IK_NotICE, E->getBeginLoc());
 
   case Expr::InitListExprClass: {
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index db8cdc0a33d04..b3866c0de11f7 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -4945,8 +4945,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   case Expr::CXXInheritedCtorInitExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::PackIndexingExprClass:
-  case Expr::CXXExpansionInitListSelectExprClass:
-  case Expr::CXXDestructuringExpansionSelectExprClass:
+  case Expr::CXXExpansionSelectExprClass:
     llvm_unreachable("unexpected statement kind");
 
   case Expr::ConstantExprClass:
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index d2f8d62582152..6032e40ec5ca5 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -461,6 +461,8 @@ void StmtPrinter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node) {
     PrintExpr(Node->getRangeVar()->getInit());
   else if (Node->isDependent())
     PrintExpr(Node->getExpansionInitializer());
+  else if (Node->isDestructuring())
+    PrintExpr(Node->getDecompositionDecl()->getInit());
   else
     PrintExpr(Node->getExpansionVariable()->getInit());
 
@@ -473,16 +475,11 @@ void StmtPrinter::VisitCXXExpansionStmtInstantiation(
   llvm_unreachable("should never be printed");
 }
 
-void StmtPrinter::VisitCXXExpansionInitListSelectExpr(
-    CXXExpansionInitListSelectExpr *Node) {
+void StmtPrinter::VisitCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *Node) {
   PrintExpr(Node->getRangeExpr());
 }
 
-void StmtPrinter::VisitCXXDestructuringExpansionSelectExpr(
-    CXXDestructuringExpansionSelectExpr *Node) {
-  PrintExpr(Node->getDecompositionDecl()->getInit());
-}
-
 void StmtPrinter::VisitMSDependentExistsStmt(MSDependentExistsStmt *Node) {
   Indent();
   if (Node->isIfExists())
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 767479be115f5..3500d39e0c703 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2411,17 +2411,11 @@ void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) {
 
 void StmtProfiler::VisitEmbedExpr(const EmbedExpr *E) { VisitExpr(E); }
 
-void StmtProfiler::VisitCXXExpansionInitListSelectExpr(
-    const CXXExpansionInitListSelectExpr *E) {
+void StmtProfiler::VisitCXXExpansionSelectExpr(
+    const CXXExpansionSelectExpr *E) {
   VisitExpr(E);
 }
 
-void StmtProfiler::VisitCXXDestructuringExpansionSelectExpr(
-    const CXXDestructuringExpansionSelectExpr *E) {
-  VisitExpr(E);
-  VisitDecl(E->getDecompositionDecl());
-}
-
 void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); }
 
 void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 453bcaa5de40f..a88d152c4c507 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1854,11 +1854,6 @@ void TextNodeDumper::VisitCXXDependentScopeMemberExpr(
   OS << " " << (Node->isArrow() ? "->" : ".") << Node->getMember();
 }
 
-void TextNodeDumper::VisitCXXDestructuringExpansionSelectExpr(
-    const CXXDestructuringExpansionSelectExpr *Node) {
-  dumpDeclRef(Node->getDecompositionDecl());
-}
-
 void TextNodeDumper::VisitObjCMessageExpr(const ObjCMessageExpr *Node) {
   OS << " selector=";
   Node->getSelector().print(OS);
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 9c072fa07d1c4..53603b76011ae 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1288,8 +1288,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::ConvertVectorExprClass:
   case Expr::VAArgExprClass:
   case Expr::CXXParenListInitExprClass:
-  case Expr::CXXExpansionInitListSelectExprClass:
-  case Expr::CXXDestructuringExpansionSelectExprClass:
+  case Expr::CXXExpansionSelectExprClass:
     return canSubStmtsThrow(*this, S);
 
   case Expr::CompoundLiteralExprClass:
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 39afc5692b42f..df25b1701207f 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9305,14 +9305,8 @@ StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
 }
 
 template <typename Derived>
-ExprResult TreeTransform<Derived>::TransformCXXExpansionInitListSelectExpr(
-    CXXExpansionInitListSelectExpr *E) {
-  llvm_unreachable("TOOD");
-}
-
-template <typename Derived>
-ExprResult TreeTransform<Derived>::TransformCXXDestructuringExpansionSelectExpr(
-    CXXDestructuringExpansionSelectExpr *E) {
+ExprResult TreeTransform<Derived>::TransformCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *E) {
   llvm_unreachable("TOOD");
 }
 
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 9b91f56fa3ff6..5c65be8c594ef 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1752,20 +1752,13 @@ void ASTStmtReader::VisitCXXExpansionStmtInstantiation(
   S->setShouldApplyLifetimeExtensionToSharedStmts(Record.readBool());
 }
 
-void ASTStmtReader::VisitCXXExpansionInitListSelectExpr(
-    CXXExpansionInitListSelectExpr *E) {
+void ASTStmtReader::VisitCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *E) {
   VisitExpr(E);
   E->setRangeExpr(cast<InitListExpr>(Record.readSubExpr()));
   E->setIndexExpr(Record.readSubExpr());
 }
 
-void ASTStmtReader::VisitCXXDestructuringExpansionSelectExpr(
-    CXXDestructuringExpansionSelectExpr *E) {
-  VisitExpr(E);
-  E->setDecompositionDecl(cast<DecompositionDecl>(Record.readDeclRef()));
-  E->setIndexExpr(Record.readSubExpr());
-}
-
 void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
   VisitStmt(S);
   S->KeywordLoc = readSourceLocation();
@@ -4493,12 +4486,8 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
     }
 
-    case EXPR_CXX_EXPANSION_INIT_LIST_SELECT:
-      S = new (Context) CXXExpansionInitListSelectExpr(Empty);
-      break;
-
-    case EXPR_CXX_DESTRUCTURING_EXPANSION_SELECT:
-      S = new (Context) CXXDestructuringExpansionSelectExpr(Empty);
+    case EXPR_CXX_EXPANSION_SELECT:
+      S = new (Context) CXXExpansionSelectExpr(Empty);
       break;
 
     case STMT_OPENACC_COMPUTE_CONSTRUCT: {
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 8b36dea086427..2b223d1f15c53 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1729,20 +1729,12 @@ void ASTStmtWriter::VisitCXXExpansionStmtInstantiation(
   Code = serialization::STMT_CXX_EXPANSION_INSTANTIATION;
 }
 
-void ASTStmtWriter::VisitCXXExpansionInitListSelectExpr(
-    CXXExpansionInitListSelectExpr *E) {
+void ASTStmtWriter::VisitCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *E) {
   VisitExpr(E);
   Record.AddStmt(E->getRangeExpr());
   Record.AddStmt(E->getIndexExpr());
-  Code = serialization::EXPR_CXX_EXPANSION_INIT_LIST_SELECT;
-}
-
-void ASTStmtWriter::VisitCXXDestructuringExpansionSelectExpr(
-    CXXDestructuringExpansionSelectExpr *E) {
-  VisitExpr(E);
-  Record.AddDeclRef(E->getDecompositionDecl());
-  Record.AddStmt(E->getIndexExpr());
-  Code = serialization::EXPR_CXX_DESTRUCTURING_EXPANSION_SELECT;
+  Code = serialization::EXPR_CXX_EXPANSION_SELECT;
 }
 
 void ASTStmtWriter::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 3e6f3a7b9090e..f7cf8c2b0b777 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1754,8 +1754,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::SEHFinallyStmtClass:
     case Stmt::CXXExpansionStmtPatternClass:
     case Stmt::CXXExpansionStmtInstantiationClass:
-    case Stmt::CXXExpansionInitListSelectExprClass:
-    case Stmt::CXXDestructuringExpansionSelectExprClass:
+    case Stmt::CXXExpansionSelectExprClass:
     case Stmt::OMPCanonicalLoopClass:
     case Stmt::OMPParallelDirectiveClass:
     case Stmt::OMPSimdDirectiveClass:
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index 16e02f5a79e01..264a79f5d3a48 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -340,8 +340,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
   case Stmt::EmbedExprClass:
   case Stmt::HLSLOutArgExprClass:
   case Stmt::OpenACCAsteriskSizeExprClass:
-  case Stmt::CXXExpansionInitListSelectExprClass:
-  case Stmt::CXXDestructuringExpansionSelectExprClass:
+  case Stmt::CXXExpansionSelectExprClass:
     K = CXCursor_UnexposedExpr;
     break;
 



More information about the cfe-commits mailing list