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

>From c41aa76173c252de8fc11413e07872ba96448856 Mon Sep 17 00:00:00 2001
From: Sirraide <[email protected]>
Date: Tue, 25 Nov 2025 17:18:05 +0100
Subject: [PATCH] [Clang] [C++26] Expansion Statements (Part 1)

---
 clang/include/clang/AST/ASTNodeTraverser.h    |   6 +
 clang/include/clang/AST/Decl.h                |   6 +-
 clang/include/clang/AST/DeclBase.h            |  13 +
 clang/include/clang/AST/DeclTemplate.h        | 125 ++++
 clang/include/clang/AST/ExprCXX.h             |  44 ++
 clang/include/clang/AST/RecursiveASTVisitor.h |  12 +
 clang/include/clang/AST/StmtCXX.h             | 576 ++++++++++++++++++
 clang/include/clang/AST/TextNodeDumper.h      |   3 +
 clang/include/clang/Basic/DeclNodes.td        |   1 +
 clang/include/clang/Basic/StmtNodes.td        |   8 +
 clang/include/clang/Sema/Scope.h              |  18 +
 .../include/clang/Serialization/ASTBitCodes.h |  10 +
 clang/lib/AST/ASTImporter.cpp                 | 120 ++++
 clang/lib/AST/DeclBase.cpp                    |  14 +-
 clang/lib/AST/DeclPrinter.cpp                 |   6 +
 clang/lib/AST/DeclTemplate.cpp                |  23 +
 clang/lib/AST/Expr.cpp                        |   1 +
 clang/lib/AST/ExprCXX.cpp                     |  12 +
 clang/lib/AST/ExprClassification.cpp          |   1 +
 clang/lib/AST/ExprConstant.cpp                |   1 +
 clang/lib/AST/ItaniumMangle.cpp               |   8 +-
 clang/lib/AST/StmtCXX.cpp                     | 157 +++++
 clang/lib/AST/StmtPrinter.cpp                 |  35 +-
 clang/lib/AST/StmtProfile.cpp                 |  16 +
 clang/lib/AST/TextNodeDumper.cpp              |  32 +-
 clang/lib/CodeGen/CGDecl.cpp                  |   3 +
 clang/lib/CodeGen/CGStmt.cpp                  |   4 +
 clang/lib/Sema/IdentifierResolver.cpp         |   7 +-
 clang/lib/Sema/Scope.cpp                      |   1 +
 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 +-
 clang/lib/Sema/SemaStmtAttr.cpp               |  12 +
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   5 +
 clang/lib/Sema/TreeTransform.h                |  18 +
 clang/lib/Serialization/ASTCommon.cpp         |   1 +
 clang/lib/Serialization/ASTReaderDecl.cpp     |  12 +
 clang/lib/Serialization/ASTReaderStmt.cpp     |  47 ++
 clang/lib/Serialization/ASTWriterDecl.cpp     |   9 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |  33 +
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |   3 +
 clang/tools/libclang/CIndex.cpp               |   1 +
 clang/tools/libclang/CXCursor.cpp             |   3 +
 47 files changed, 1435 insertions(+), 29 deletions(-)

diff --git a/clang/include/clang/AST/ASTNodeTraverser.h 
b/clang/include/clang/AST/ASTNodeTraverser.h
index 3be24ff868c2d..c999b79c3b2a7 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -972,6 +972,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/Decl.h b/clang/include/clang/AST/Decl.h
index 076d9ba935583..0224cf5a3612b 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1262,14 +1262,16 @@ 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 {
     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..3a416f83d1d8f 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3343,6 +3343,131 @@ 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.
+/// 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.
+///
+/// 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.
+///
+/// Additionally, there is a 'NonTypeTemplateParmDecl', which is a template
+/// parameter that serves as the expansion index, e.g. during the N-th
+/// expansion, it is set to 'N'. See the documentation of
+/// 'CXXExpansionStmtPattern', for more information on how this is used.
+///
+/// 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.
+///
+/// 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
+///   std::tuple<int, int, int> a{1, 2, 3};
+///   template for (auto x : a) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// 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.
+/// 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
+///
+/// See the documentation around 'CXXExpansionStmtInstantiation' for more notes
+/// as to why this node exist and how it is used.
+///
+/// \see CXXExpansionStmtPattern
+/// \see CXXExpansionStmtInstantiation
+class CXXExpansionStmtDecl : public Decl, public DeclContext {
+  CXXExpansionStmtPattern *Pattern = nullptr;
+  NonTypeTemplateParmDecl *IndexNTTP = nullptr;
+  CXXExpansionStmtInstantiation *Instantiations = nullptr;
+
+  CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
+                       NonTypeTemplateParmDecl *NTTP);
+
+public:
+  friend class ASTDeclReader;
+
+  static CXXExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC,
+                                      SourceLocation Loc,
+                                      NonTypeTemplateParmDecl *NTTP);
+  static CXXExpansionStmtDecl *CreateDeserialized(ASTContext &C,
+                                                  GlobalDeclID ID);
+
+  CXXExpansionStmtPattern *getExpansionPattern() { return Pattern; }
+  const CXXExpansionStmtPattern *getExpansionPattern() const {
+    return Pattern;
+  }
+  void setExpansionPattern(CXXExpansionStmtPattern *S) { Pattern = S; }
+
+  CXXExpansionStmtInstantiation *getInstantiations() { return Instantiations; }
+  const CXXExpansionStmtInstantiation *getInstantiations() const {
+    return Instantiations;
+  }
+
+  void setInstantiations(CXXExpansionStmtInstantiation *S) {
+    Instantiations = S;
+  }
+
+  NonTypeTemplateParmDecl *getIndexTemplateParm() { return IndexNTTP; }
+  const NonTypeTemplateParmDecl *getIndexTemplateParm() const {
+    return IndexNTTP;
+  }
+
+  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 c40cd929b7408..44f2559062943 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5554,6 +5554,50 @@ class CXXReflectExpr : public Expr {
   }
 };
 
+/// Helper that selects an expression from an InitListExpr depending on the
+/// current expansion index. See 'CXXExpansionStmtPattern' for how this is 
used.
+class CXXExpansionSelectExpr : public Expr {
+  friend class ASTStmtReader;
+
+  enum SubExpr { RANGE, INDEX, COUNT };
+  Expr *SubExprs[COUNT];
+
+public:
+  CXXExpansionSelectExpr(EmptyShell Empty);
+  CXXExpansionSelectExpr(const ASTContext &C, InitListExpr *Range, Expr *Idx);
+
+  InitListExpr *getRangeExpr() {
+    return cast<InitListExpr>(SubExprs[RANGE]);
+  }
+
+  const InitListExpr *getRangeExpr() const {
+    return cast<InitListExpr>(SubExprs[RANGE]);
+  }
+
+  void setRangeExpr(InitListExpr *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() == CXXExpansionSelectExprClass;
+  }
+};
 } // 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 ce6ad723191e0..eafbe0f6c23ad 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1887,6 +1887,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));
@@ -3136,6 +3144,10 @@ DEF_TRAVERSE_STMT(RequiresExpr, {
     TRY_TO(TraverseConceptRequirement(Req));
 })
 
+DEF_TRAVERSE_STMT(CXXExpansionStmtPattern, {})
+DEF_TRAVERSE_STMT(CXXExpansionStmtInstantiation, {})
+DEF_TRAVERSE_STMT(CXXExpansionSelectExpr, {})
+
 // 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..7c955dd6091c5 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,581 @@ class CoreturnStmt : public Stmt {
   }
 };
 
+/// CXXExpansionStmtPattern - Represents an unexpanded C++ expansion statement.
+///
+/// There are four kinds of expansion statements.
+///
+/// 1. Enumerating expansion statements.
+/// 2. Iterating expansion statements.
+/// 3. Destructuring expansion statements.
+/// 4. Dependent expansion statements.
+///
+/// 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
+/// a group of expressions. The expansion initializer of this is always a
+/// syntactic-form 'InitListExpr'.
+///
+/// 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 an 'InitListExpr'. This node
+/// handles storing (and pack-expanding) the individual expressions.
+///
+/// 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 'BuildCXXExpansionSelectExpr()': for
+/// example, during the 2nd expansion of '{ a, b, c }', I is equal to 1, and
+/// BuildCXXExpansionSelectExpr(), when called via TreeTransform,
+/// 'instantiates' the expression '{ a, b, c }' to just 'b'.
+///
+/// 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].
+///
+/// Specifically, let E denote the expansion-initializer; the expansion
+/// statement is iterating if the type of E is not an array type, and either
+///
+///   2a. 'E.begin' and 'E.end' *exist* (irrespective of whether they're
+///        accessible, deleted, or even callable), or
+///
+///   2b. ADL for 'begin(E)' and 'end(E)' finds at least one viable function.
+///
+/// If neither A nor B apply to E (or if E is an array type), we treat this as
+/// a destructuring expansion statement instead (see case 3 below).
+///
+/// Notably, case 2a only checks whether the 'begin' and 'end' members exist 
and
+/// does *not* perform proper overload resolution; this is because if there is
+/// a begin/end function, but it for some reason is not usable (e.g. because it
+/// is non-const but E is const), then we'd rather error and tell the user that
+/// their begin/end function is wrong rather than falling back to 
destructuring.
+///
+/// Conversely, case 2b *does* perform overload resolution, simply because ADL
+/// may find quite a few begin/end overloads for unrelated types that happen to
+/// be in the same namespace. E.g. if the type of E is 'std::tuple', then there
+/// are quite a few begin/end pairs in the namespace 'std', but non of them can
+/// actually be used for a 'std::tuple', and we definitely want to destructure 
a
+/// tuple rather than error about it not being iterable.
+///
+/// In either case, once we've decided that the expansion statement is indeed
+/// iterating, we *do* make sure that the expression 'E.begin()'/'begin(E)' is
+/// well-formed, but any error at that point is a hard error and does not make
+/// us switch to destructuring instead.
+///
+/// The result of this expression is stored in a variable 'begin', which is 
then
+/// used to compute another variable 'iter' (which is just 'begin' + the
+/// expansion index) during expansion. During the N-th expansion, the expansion
+/// variable is then set to '*iter'. See [stmt.expand] for more information.
+///
+/// The expression used to compute the size of the expansion is not stored and
+/// is only created at the moment of expansion. See 
Sema::ComputeExpansionSize()
+/// for more information about this.
+///
+/// Example:
+/// \verbatim
+///   static constexpr std::string_view foo = "abcd";
+///   template for (auto x : foo) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// Here, 'begin' is 'foo.begin()', and during e.g. the 0-th expansion, 'iter'
+/// is 'begin + 0', and thus '*iter' yields 'a', which results in 'x' being
+/// a variable of type 'char' with value 'a'.
+///
+/// 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 declaration, with the number of
+/// bindings and expansion size determined by the usual means (array size,
+/// std::tuple_size, etc.).
+///
+/// During the N-th expansion, the expansion variable is then initialized with
+/// the N-th binding of the structured-binding declaration. This is implemented
+/// by wrapping the initializer with a CXXExpansionSelectExpr, which selects a
+/// binding based on the current expansion index when called from 
TreeTransform.
+///
+/// Example:
+/// \verbatim
+///   std::tuple<int, long, unsigned> a {1, 2l, 3u};
+///   template for (auto x : a) {
+///     // ...
+///   }
+/// \endverbatim
+///
+/// Here, we build 'auto [_U0, _U1, _U2] = a', and during e.g. the 0-th
+/// expansion, 'x' is initialized with '_U0'.
+///
+/// 4. Represents an expansion statement whose expansion-initializer is
+/// type-dependent.
+///
+/// This will eventually become an iterating or destructuring expansion
+/// statement once the expansion-initializer is no longer dependent.
+///
+/// Dependent expansion statements can never be enumerating: even if the
+/// expansion size of an enumerating expansion statement is dependent (which
+/// is possible if the expression-list contains a pack), we still don't build
+/// an 'Enumerating' 'CXXExpansionStmtPattern' for it.
+///
+/// Example:
+/// \verbatim
+///   template <typename T>
+///   void f() {
+///     template for (auto x : T()) {
+///       // ...
+///     }
+///   }
+/// \endverbatim
+///
+/// \see CXXExpansionStmtDecl for more documentation on expansion statements.
+class CXXExpansionStmtPattern final
+    : public Stmt,
+      llvm::TrailingObjects<CXXExpansionStmtPattern, Stmt *> {
+  friend class ASTStmtReader;
+  friend TrailingObjects;
+
+public:
+  enum class ExpansionStmtKind : uint8_t {
+    Enumerating,
+    Iterating,
+    Destructuring,
+    Dependent,
+  };
+
+private:
+  ExpansionStmtKind PatternKind;
+  SourceLocation LParenLoc;
+  SourceLocation ColonLoc;
+  SourceLocation RParenLoc;
+  CXXExpansionStmtDecl *ParentDecl;
+
+  /// Substatements of an unexpanded expansion statement.
+  ///
+  /// 'INIT', 'VAR', and 'BODY' are common to all kinds of expansion 
statements;
+  /// the former may be null if there is no init-statement.
+  ///
+  /// Depending on the kind of expansion statement, we may have to store
+  /// additional sub-statements, the first of which is denoted by
+  /// 'FIRST_CHILD_STATEMENT'.
+  ///
+  /// All of the sub-statements are allocated as 'TrailingObjects' so we can
+  /// return a single contiguous range from 'children()'.
+  enum SubStmt {
+    INIT,
+    VAR,
+    BODY,
+    FIRST_CHILD_STMT,
+
+    // Enumerating expansion statement (no additional sub-statements).
+    COUNT_Enumerating = FIRST_CHILD_STMT,
+
+    // Dependent expansion statement (1 additional sub-statement).
+    EXPANSION_INITIALIZER = FIRST_CHILD_STMT,
+    COUNT_Dependent,
+
+    // Destructuring expansion statement (1 additional sub-statement).
+    DECOMP_DECL = FIRST_CHILD_STMT,
+    COUNT_Destructuring,
+
+    // Iterating expansion statement (3 additional sub-statements).
+    RANGE = FIRST_CHILD_STMT,
+    BEGIN,
+    ITER,
+    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* Iter, 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;
+  }
+
+  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;
+  }
+
+  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();
+  }
+
+  DeclStmt *getExpansionVarStmt() { return cast<DeclStmt>(getSubStmt(VAR)); }
+  const DeclStmt *getExpansionVarStmt() const {
+    return cast<DeclStmt>(getSubStmt(VAR));
+  }
+
+  void setExpansionVarStmt(Stmt *S) { getSubStmt(VAR) = S; }
+
+  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 {
+    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;
+  }
+
+  const VarDecl *getRangeVar() const {
+    assert(isIterating());
+    return cast<VarDecl>(getRangeVarStmt()->getSingleDecl());
+  }
+
+  VarDecl *getRangeVar() {
+    assert(isIterating());
+    return cast<VarDecl>(getRangeVarStmt()->getSingleDecl());
+  }
+
+  const DeclStmt *getBeginVarStmt() const {
+    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;
+  }
+
+  const VarDecl *getBeginVar() const {
+    assert(isIterating());
+    return cast<VarDecl>(getBeginVarStmt()->getSingleDecl());
+  }
+
+  VarDecl *getBeginVar() {
+    assert(isIterating());
+    return cast<VarDecl>(getBeginVarStmt()->getSingleDecl());
+  }
+
+  const DeclStmt *getIterVarStmt() const {
+    assert(isIterating());
+    return cast<DeclStmt>(getSubStmt(ITER));
+  }
+
+  DeclStmt *getIterVarStmt() {
+    assert(isIterating());
+    return cast<DeclStmt>(getSubStmt(ITER));
+  }
+
+  void setIterVarStmt(DeclStmt *S) {
+    assert(isIterating());
+    getSubStmt(ITER) = S;
+  }
+
+  const VarDecl *getIterVar() const {
+    assert(isIterating());
+    return cast<VarDecl>(getIterVarStmt()->getSingleDecl());
+  }
+
+  VarDecl *getIterVar() {
+    assert(isIterating());
+    return cast<VarDecl>(getIterVarStmt()->getSingleDecl());
+  }
+
+
+  // Accessors for destructuring statements.
+  Stmt *getDecompositionDeclStmt() {
+    assert(isDestructuring());
+    return getSubStmt(DECOMP_DECL);
+  }
+
+  const Stmt *getDecompositionDeclStmt() const {
+    assert(isDestructuring());
+    return getSubStmt(DECOMP_DECL);
+  }
+
+  void setDecompositionDeclStmt(Stmt *S) {
+    assert(isDestructuring());
+    getSubStmt(DECOMP_DECL) = S;
+  }
+
+  DecompositionDecl *getDecompositionDecl();
+  const DecompositionDecl *getDecompositionDecl() const {
+    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(getTrailingObjects(),
+                       getTrailingObjects() + getNumSubStmts());
+  }
+
+  const_child_range children() const {
+    return const_child_range(getTrailingObjects(),
+                             getTrailingObjects() + getNumSubStmts());
+  }
+
+  static bool classof(const Stmt *T) {
+    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];
+  }
+};
+
+/// 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. 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
+///
+/// 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
+///
+/// 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 *> {
+  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 32e83ebb5c8eb..a3e1eb4140c34 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -266,6 +266,9 @@ 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);
   void VisitCallExpr(const CallExpr *Node);
   void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *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 61d76bafdfcde..8f0d875b58d99 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -60,6 +60,11 @@ def CXXForRangeStmt : StmtNode<Stmt>;
 def CoroutineBodyStmt : StmtNode<Stmt>;
 def CoreturnStmt : StmtNode<Stmt>;
 
+// C++ expansion statements (P1306)
+def CXXExpansionStmtPattern : StmtNode<Stmt>;
+def CXXExpansionStmtInstantiation
+    : StmtNode<Stmt>; // *Not* derived from CXXExpansionStmtPattern!
+
 // Expressions
 def Expr : StmtNode<ValueStmt, 1>;
 def PredefinedExpr : StmtNode<Expr>;
@@ -184,6 +189,9 @@ def RequiresExpr : StmtNode<Expr>;
 // c++ 26 reflection
 def CXXReflectExpr : StmtNode<Expr>;
 
+// C++26 Expansion statement support expressions
+def CXXExpansionSelectExpr : StmtNode<Expr>;
+
 // Obj-C Expressions.
 def ObjCObjectLiteral : StmtNode<Expr, 1>;
 def ObjCStringLiteral : StmtNode<ObjCObjectLiteral>;
diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h
index 0d1c0ff6a1e91..fb429b73f8627 100644
--- a/clang/include/clang/Sema/Scope.h
+++ b/clang/include/clang/Sema/Scope.h
@@ -196,6 +196,16 @@ class Scope {
   /// declared in this scope.
   unsigned short PrototypeIndex;
 
+  /// IsExpansionStmtScope - This is the scope corresponding to a C++26
+  /// expansion statement.
+  ///
+  /// FIXME: This should be part of ScopeFlags, but we're out of bits, so we
+  /// need to update every place that uses 'unsigned' to hold scope flags. We
+  /// should probably redefine ScopeFlags as an 'enum class : uint64_t' and
+  /// use LLVM_MARK_AS_BITMASK_ENUM() and friends so we can continue to '|'
+  /// scope flags together.
+  bool IsExpansionStmtScope;
+
   /// FnParent - If this scope has a parent scope that is a function body, this
   /// pointer is non-null and points to it.  This is used for label processing.
   Scope *FnParent;
@@ -317,6 +327,14 @@ class Scope {
     return Flags & ConditionVarScope;
   }
 
+  void setIsExpansionStmtScope(bool Value = true) {
+    IsExpansionStmtScope = Value;
+  }
+
+  bool isExpansionStmtScope() const {
+    return IsExpansionStmtScope;
+  }
+
   /// getBreakParent - Return the closest scope that a break statement
   /// would be affected by.
   Scope *getBreakParent() {
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index 2c394fd03e8ef..6589da6a04549 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1466,6 +1466,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,
 
@@ -1845,6 +1848,12 @@ enum StmtCode {
 
   STMT_CXX_FOR_RANGE,
 
+  /// A CXXExpansionPatternStmt.
+  STMT_CXX_EXPANSION_PATTERN,
+
+  /// A CXXExpansionInstantiationStmt.
+  STMT_CXX_EXPANSION_INSTANTIATION,
+
   /// A CXXOperatorCallExpr record.
   EXPR_CXX_OPERATOR_CALL,
 
@@ -1936,6 +1945,7 @@ enum StmtCode {
   EXPR_CXX_FOLD,                          // CXXFoldExpr
   EXPR_CONCEPT_SPECIALIZATION,            // ConceptSpecializationExpr
   EXPR_REQUIRES,                          // RequiresExpr
+  EXPR_CXX_EXPANSION_SELECT,              // CXXExpansionSelectExpr
 
   // Reflection
   EXPR_REFLECT,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 41ba98c53247d..be8acb46ef151 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,9 @@ namespace clang {
     ExpectedStmt VisitCXXCatchStmt(CXXCatchStmt *S);
     ExpectedStmt VisitCXXTryStmt(CXXTryStmt *S);
     ExpectedStmt VisitCXXForRangeStmt(CXXForRangeStmt *S);
+    ExpectedStmt VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S);
+    ExpectedStmt
+    VisitCXXExpansionStmtInstantiation(CXXExpansionStmtInstantiation *S);
     // FIXME: MSDependentExistsStmt
     ExpectedStmt VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
     ExpectedStmt VisitObjCAtCatchStmt(ObjCAtCatchStmt *S);
@@ -700,6 +704,7 @@ namespace clang {
     VisitSubstNonTypeTemplateParmPackExpr(SubstNonTypeTemplateParmPackExpr *E);
     ExpectedStmt VisitPseudoObjectExpr(PseudoObjectExpr *E);
     ExpectedStmt VisitCXXParenListInitExpr(CXXParenListInitExpr *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
@@ -2863,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 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,
+                              ToIndex))
+    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;
@@ -7463,6 +7496,81 @@ ExpectedStmt 
ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
       ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc);
 }
 
+ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtPattern(
+    CXXExpansionStmtPattern *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);
+
+  switch (S->getKind()) {
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating:
+    return CXXExpansionStmtPattern::CreateEnumerating(
+        Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToLParenLoc,
+        ToColonLoc, ToRParenLoc);
+
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: {
+    auto ToRange = importChecked(Err, S->getRangeVarStmt());
+    auto ToBegin = importChecked(Err, S->getBeginVarStmt());
+    auto ToIter = importChecked(Err, S->getIterVarStmt());
+    if (Err)
+      return std::move(Err);
+
+    return CXXExpansionStmtPattern::CreateIterating(
+        Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToRange,
+        ToBegin, ToIter, 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();
+  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();
@@ -9359,6 +9467,18 @@ 
ASTNodeImporter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
                                       ToInitLoc, ToBeginLoc, ToEndLoc);
 }
 
+ExpectedStmt ASTNodeImporter::VisitCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *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())
+      CXXExpansionSelectExpr(Importer.getToContext(), ToRange, ToIndex);
+}
+
 Error ASTNodeImporter::ImportOverriddenMethods(CXXMethodDecl *ToMethod,
                                                CXXMethodDecl *FromMethod) {
   Error ImportErrors = Error::success();
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 0a1e442656c35..e81bea6302456 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 5e377a6c0c247..fb5631c8673b0 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -114,6 +114,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);
@@ -1347,6 +1348,11 @@ void 
DeclPrinter::VisitClassTemplatePartialSpecializationDecl(
   VisitCXXRecordDecl(D);
 }
 
+void DeclPrinter::VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *D) {
+  D->getExpansionPattern()->printPretty(Out, /*PrinterHelper=*/nullptr, Policy,
+                                        Indentation, "\n", &Context);
+}
+
 //----------------------------------------------------------------------------
 // Objective-C declarations
 //----------------------------------------------------------------------------
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 5a8e1ed445f3a..7d0a214e25813 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1711,6 +1711,9 @@ clang::getReplacedTemplateParameter(Decl *D, unsigned 
Index) {
     return getReplacedTemplateParameter(
         cast<FunctionDecl>(D)->getTemplateSpecializationInfo()->getTemplate(),
         Index);
+  case Decl::Kind::CXXExpansionStmt:
+    assert(Index == 0 && "expansion stmts only have a single template param");
+    return {cast<CXXExpansionStmtDecl>(D)->getIndexTemplateParm(), {}};
   default:
     llvm_unreachable("Unhandled templated declaration kind");
   }
@@ -1782,3 +1785,23 @@ const Decl &clang::adjustDeclToTemplate(const Decl &D) {
   // FIXME: Adjust alias templates?
   return D;
 }
+
+CXXExpansionStmtDecl::CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
+                                           NonTypeTemplateParmDecl *NTTP)
+    : Decl(CXXExpansionStmt, DC, Loc), DeclContext(CXXExpansionStmt),
+      IndexNTTP(NTTP) {}
+
+CXXExpansionStmtDecl *
+CXXExpansionStmtDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation 
Loc,
+                             NonTypeTemplateParmDecl *NTTP) {
+  return new (C, DC) CXXExpansionStmtDecl(DC, Loc, NTTP);
+}
+CXXExpansionStmtDecl *
+CXXExpansionStmtDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
+  return new (C, ID)
+      CXXExpansionStmtDecl(/*DC=*/nullptr, SourceLocation(), /*NTTP=*/nullptr);
+}
+
+SourceRange CXXExpansionStmtDecl::getSourceRange() const {
+  return Pattern ? Pattern->getSourceRange() : SourceRange();
+}
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 64d61dbc3d128..ba2f97b5b4b8d 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3711,6 +3711,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
   case FunctionParmPackExprClass:
   case RecoveryExprClass:
   case CXXFoldExprClass:
+  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 dd603bf548926..c50ff64f9de64 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -2036,3 +2036,15 @@ CXXFoldExpr::CXXFoldExpr(QualType T, 
UnresolvedLookupExpr *Callee,
   SubExprs[SubExpr::RHS] = RHS;
   setDependence(computeDependence(this));
 }
+
+CXXExpansionSelectExpr::CXXExpansionSelectExpr(EmptyShell Empty)
+    : Expr(CXXExpansionSelectExprClass, Empty) {}
+
+CXXExpansionSelectExpr::CXXExpansionSelectExpr(
+    const ASTContext &C, InitListExpr *Range, Expr *Idx)
+    : Expr(CXXExpansionSelectExprClass, C.DependentTy, VK_PRValue,
+           OK_Ordinary) {
+  setDependence(ExprDependence::TypeValueInstantiation);
+  SubExprs[RANGE] = Range;
+  SubExprs[INDEX] = Idx;
+}
diff --git a/clang/lib/AST/ExprClassification.cpp 
b/clang/lib/AST/ExprClassification.cpp
index a83c17074ea69..502a681ddf2a0 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -218,6 +218,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const 
Expr *E) {
   case Expr::ConceptSpecializationExprClass:
   case Expr::RequiresExprClass:
   case Expr::CXXReflectExprClass:
+  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 4f45fa728c605..a464ed90f70b8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -21423,6 +21423,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext 
&Ctx) {
   case Expr::SYCLUniqueStableNameExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::HLSLOutArgExprClass:
+  case Expr::CXXExpansionSelectExprClass:
     return ICEDiag(IK_NotICE, E->getBeginLoc());
 
   case Expr::MemberExprClass: {
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index ccf5717073fbf..ab1353321635b 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -45,7 +45,7 @@ namespace UnsupportedItaniumManglingKind =
 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) {
@@ -1876,6 +1876,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;
@@ -2217,6 +2219,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;
@@ -4977,6 +4982,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, 
unsigned Arity,
   case Expr::CXXInheritedCtorInitExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::PackIndexingExprClass:
+  case Expr::CXXExpansionSelectExprClass:
     llvm_unreachable("unexpected statement kind");
 
   case Expr::ConstantExprClass:
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index 6a69fe75136f3..e7b9b8ad3e64e 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,159 @@ 
CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args)
       Args.ReturnStmtOnAllocFailure;
   llvm::copy(Args.ParamMoves, const_cast<Stmt **>(getParamMoves().data()));
 }
+
+CXXExpansionStmtPattern::CXXExpansionStmtPattern(ExpansionStmtKind PatternKind,
+                                                 EmptyShell Empty)
+    : Stmt(CXXExpansionStmtPatternClass, Empty), PatternKind(PatternKind) {}
+
+CXXExpansionStmtPattern::CXXExpansionStmtPattern(
+    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);
+}
+
+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)...);
+}
+
+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;
+}
+
+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;
+}
+
+CXXExpansionStmtPattern *
+CXXExpansionStmtPattern::CreateEmpty(ASTContext &Context, EmptyShell Empty,
+                                     ExpansionStmtKind Kind) {
+  return AllocateAndConstruct(Context, Kind, Empty);
+}
+
+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);
+}
+
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateIterating(
+    ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+    DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin,
+    DeclStmt *Iter, 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->setIterVarStmt(Iter);
+  return Pattern;
+}
+
+SourceLocation CXXExpansionStmtPattern::getBeginLoc() const {
+  return ParentDecl->getLocation();
+}
+
+DecompositionDecl *
+CXXExpansionStmtPattern::getDecompositionDecl() {
+  assert(isDestructuring());
+  return cast<DecompositionDecl>(
+      cast<DeclStmt>(getDecompositionDeclStmt())->getSingleDecl());
+}
+
+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(
+    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 4d364fdcd5502..1cc7a20c0e4bf 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -263,7 +263,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 +448,38 @@ void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt 
*Node) {
   PrintControlledStmt(Node->getBody());
 }
 
+void StmtPrinter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node) {
+  OS << "template for (";
+  if (Node->getInit())
+    PrintInitStmt(Node->getInit(), 14);
+  PrintingPolicy SubPolicy(Policy);
+  SubPolicy.SuppressInitializers = true;
+  Node->getExpansionVariable()->print(OS, SubPolicy, IndentLevel);
+  OS << " : ";
+
+  if (Node->isIterating())
+    PrintExpr(Node->getRangeVar()->getInit());
+  else if (Node->isDependent())
+    PrintExpr(Node->getExpansionInitializer());
+  else if (Node->isDestructuring())
+    PrintExpr(Node->getDecompositionDecl()->getInit());
+  else
+    PrintExpr(Node->getExpansionVariable()->getInit());
+
+  OS << ")";
+  PrintControlledStmt(Node->getBody());
+}
+
+void StmtPrinter::VisitCXXExpansionStmtInstantiation(
+    CXXExpansionStmtInstantiation *) {
+  llvm_unreachable("should never be printed");
+}
+
+void StmtPrinter::VisitCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *Node) {
+  PrintExpr(Node->getRangeExpr());
+}
+
 void StmtPrinter::VisitMSDependentExistsStmt(MSDependentExistsStmt *Node) {
   Indent();
   if (Node->isIfExists())
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index e8c1f8a8ecb5f..43cff54c23f89 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -366,6 +366,17 @@ void StmtProfiler::VisitCXXForRangeStmt(const 
CXXForRangeStmt *S) {
   VisitStmt(S);
 }
 
+void StmtProfiler::VisitCXXExpansionStmtPattern(
+    const CXXExpansionStmtPattern *S) {
+  VisitStmt(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());
@@ -2428,6 +2439,11 @@ void StmtProfiler::VisitSourceLocExpr(const 
SourceLocExpr *E) {
 
 void StmtProfiler::VisitEmbedExpr(const EmbedExpr *E) { VisitExpr(E); }
 
+void StmtProfiler::VisitCXXExpansionSelectExpr(
+    const CXXExpansionSelectExpr *E) {
+  VisitExpr(E);
+}
+
 void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); }
 
 void StmtProfiler::VisitObjCObjectLiteral(const ObjCObjectLiteral *E) {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 250ec8b666e05..60df46685828a 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -968,7 +968,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: {
@@ -1512,6 +1516,32 @@ void clang::TextNodeDumper::VisitCoreturnStmt(const 
CoreturnStmt *Node) {
     OS << " implicit";
 }
 
+void TextNodeDumper::VisitCXXExpansionStmtPattern(
+    const CXXExpansionStmtPattern *Node) {
+  switch (Node->getKind()) {
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating:
+    OS << " enumerating";
+    return;
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
+    OS << " iterating";
+    return;
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
+    OS << " destructuring";
+    return;
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent:
+    OS << " dependent";
+    return;
+  }
+
+  llvm_unreachable("invalid expansion statement kind");
+}
+
+void TextNodeDumper::VisitCXXExpansionStmtInstantiation(
+    const CXXExpansionStmtInstantiation *Node) {
+  if (Node->shouldApplyLifetimeExtensionToSharedStmts())
+    OS << " applies_lifetime_extension";
+}
+
 void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) {
   if (Node->hasAPValueResult())
     AddChild("value",
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8b5ffde1b73f3..047aa55dc4b4a 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 a923002bec9b6..8d9eb67cc2cef 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -203,6 +203,10 @@ void CodeGenFunction::EmitStmt(const Stmt *S, 
ArrayRef<const Attr *> Attrs) {
   case Stmt::CXXForRangeStmtClass:
     EmitCXXForRangeStmt(cast<CXXForRangeStmt>(*S), Attrs);
     break;
+  case Stmt::CXXExpansionStmtPatternClass:
+    llvm_unreachable("unexpanded expansion statements should not be emitted");
+  case Stmt::CXXExpansionStmtInstantiationClass:
+    llvm_unreachable("Todo");
   case Stmt::SEHTryStmtClass:
     EmitSEHTryStmt(cast<SEHTryStmt>(*S));
     break;
diff --git a/clang/lib/Sema/IdentifierResolver.cpp 
b/clang/lib/Sema/IdentifierResolver.cpp
index 2213c3c837243..5daa42e4bc9bd 100644
--- a/clang/lib/Sema/IdentifierResolver.cpp
+++ b/clang/lib/Sema/IdentifierResolver.cpp
@@ -99,6 +99,8 @@ IdentifierResolver::~IdentifierResolver() {
 /// isDeclInScope - If 'Ctx' is a function/method, isDeclInScope returns true
 /// if 'D' is in Scope 'S', otherwise 'S' is ignored and isDeclInScope returns
 /// true if 'D' belongs to the given declaration context.
+///
+/// If 'Ctx' is an expansion statement, we use its enclosing function instead.
 bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S,
                                        bool AllowInlineNamespace) const {
   Ctx = Ctx->getRedeclContext();
@@ -107,7 +109,10 @@ bool IdentifierResolver::isDeclInScope(Decl *D, 
DeclContext *Ctx, Scope *S,
   // conflict with other Decls.
   if (LangOpt.HLSL && isa<HLSLBufferDecl>(D))
     return false;
-  if (Ctx->isFunctionOrMethod() || (S && S->isFunctionPrototypeScope())) {
+  if (Ctx->getEnclosingNonExpansionStatementContext()
+          ->getRedeclContext()
+          ->isFunctionOrMethod() ||
+      (S && S->isFunctionPrototypeScope())) {
     // Ignore the scopes associated within transparent declaration contexts.
     while (S->getEntity() &&
            (S->getEntity()->isTransparentContext() ||
diff --git a/clang/lib/Sema/Scope.cpp b/clang/lib/Sema/Scope.cpp
index e66cce255230b..997378f3d8368 100644
--- a/clang/lib/Sema/Scope.cpp
+++ b/clang/lib/Sema/Scope.cpp
@@ -18,6 +18,7 @@
 using namespace clang;
 
 void Scope::setFlags(Scope *parent, unsigned flags) {
+  IsExpansionStmtScope = false;
   AnyParent = parent;
   Flags = flags;
 
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index ee750284e4266..50bc923ebc0a4 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1701,14 +1701,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 90307423a20b6..16a34475aac1a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7467,7 +7467,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");
 }
@@ -7477,7 +7477,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 56079ea8e1bf8..cabcb3ae7bc6d 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1302,6 +1302,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::ConvertVectorExprClass:
   case Expr::VAArgExprClass:
   case Expr::CXXParenListInitExprClass:
+  case Expr::CXXExpansionSelectExprClass:
     return canSubStmtsThrow(*this, S);
 
   case Expr::CompoundLiteralExprClass:
@@ -1554,6 +1555,13 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Stmt::SwitchStmtClass:
   case Stmt::WhileStmtClass:
   case Stmt::DeferStmtClass:
+  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: {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 102b6315b4e3b..66306d172a462 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19679,11 +19679,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 f7e005a40363c..b47fbbf691133 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7645,7 +7645,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 8572e3a742a6c..db3eae4be1c41 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);
@@ -1403,7 +1400,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)))
@@ -2551,9 +2550,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 de53f6010a1b6..9502b440dbe97 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -4460,7 +4460,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/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index f7de98a3e5cf0..740d2c6a6505c 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -38,6 +38,18 @@ static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const 
ParsedAttr &A,
     return nullptr;
   }
 
+  // CWG 3045: The innermost enclosing switch statement of a fallthrough
+  // statement S shall be contained in the innermost enclosing expansion
+  // statement (8.7 [stmt.expand]) of S, if any.
+  for (Scope *Sc = S.getCurScope();
+       Sc && !Sc->isFunctionScope() && !Sc->isSwitchScope();
+       Sc = Sc->getParent()) {
+    if (Sc->isExpansionStmtScope()) {
+      S.Diag(A.getLoc(), diag::err_fallthrough_attr_invalid_placement);
+      return nullptr;
+    }
+  }
+
   // If this is spelled as the standard C++17 attribute, but not in C++17, warn
   // about using it as an extension.
   if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute() &&
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index cc24e03e77c07..d057476a012db 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2101,6 +2101,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 8ae5df367e0dd..ad8e0b76f1dcb 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9369,6 +9369,24 @@ 
TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
   return FinishCXXForRangeStmt(NewStmt.get(), Body.get());
 }
 
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
+    CXXExpansionStmtPattern *S) {
+  llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
+    CXXExpansionStmtInstantiation *S) {
+  llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *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 b49bd5ea8bca6..e0b8589af33dd 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);
@@ -2784,6 +2785,14 @@ void 
ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) {
   D->RParenLoc = readSourceLocation();
 }
 
+void ASTDeclReader::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
+  VisitDecl(D);
+  D->Pattern = cast<CXXExpansionStmtPattern>(Record.readStmt());
+  D->Instantiations =
+      cast_or_null<CXXExpansionStmtInstantiation>(Record.readStmt());
+  D->IndexNTTP = cast<NonTypeTemplateParmDecl>(Record.readDeclRef());
+}
+
 void ASTDeclReader::VisitEmptyDecl(EmptyDecl *D) {
   VisitDecl(D);
 }
@@ -4106,6 +4115,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 801eed43c2440..c929ea8ac8c82 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1775,6 +1775,35 @@ void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt 
*S) {
   S->setBody(Record.readSubStmt());
 }
 
+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());
+  for (Stmt *&SubStmt : S->children())
+    SubStmt = 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::VisitCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *E) {
+  VisitExpr(E);
+  E->setRangeExpr(cast<InitListExpr>(Record.readSubExpr()));
+  E->setIndexExpr(Record.readSubExpr());
+}
+
 void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
   VisitStmt(S);
   S->KeywordLoc = readSourceLocation();
@@ -3623,6 +3652,19 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
              /*numHandlers=*/Record[ASTStmtReader::NumStmtFields]);
       break;
 
+    case STMT_CXX_EXPANSION_PATTERN:
+      S = CXXExpansionStmtPattern::CreateEmpty(
+          Context, Empty,
+          static_cast<CXXExpansionStmtPattern::ExpansionStmtKind>(
+              Record[ASTStmtReader::NumStmtFields]));
+      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;
@@ -4500,6 +4542,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       S = new (Context) ConceptSpecializationExpr(Empty);
       break;
     }
+
+    case EXPR_CXX_EXPANSION_SELECT:
+      S = new (Context) CXXExpansionSelectExpr(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 7646d5d5efe00..71971e8ae0fcd 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);
@@ -2175,6 +2176,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.AddDeclRef(D->getIndexTemplateParm());
+  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 934a95df1be7e..e33a715b143b8 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1757,6 +1757,39 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt 
*S) {
   Code = serialization::STMT_CXX_FOR_RANGE;
 }
 
+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());
+  for (Stmt* SubStmt : S->children())
+    Record.AddStmt(SubStmt);
+  Code = serialization::STMT_CXX_EXPANSION_PATTERN;
+}
+
+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::VisitCXXExpansionSelectExpr(
+    CXXExpansionSelectExpr *E) {
+  VisitExpr(E);
+  Record.AddStmt(E->getRangeExpr());
+  Record.AddStmt(E->getIndexExpr());
+  Code = serialization::EXPR_CXX_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 e9522a7975515..76a682bf983cf 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1745,6 +1745,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::SEHExceptStmtClass:
     case Stmt::SEHLeaveStmtClass:
     case Stmt::SEHFinallyStmtClass:
+    case Stmt::CXXExpansionStmtPatternClass:
+    case Stmt::CXXExpansionStmtInstantiationClass:
+    case Stmt::CXXExpansionSelectExprClass:
     case Stmt::OMPCanonicalLoopClass:
     case Stmt::OMPParallelDirectiveClass:
     case Stmt::OMPSimdDirectiveClass:
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 3ee37ed2dfc27..a6d53af8e4eda 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -7282,6 +7282,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 d31d2c0c9bb67..08624d4ce3f73 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -295,6 +295,8 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl 
*Parent,
 
   case Stmt::CoroutineBodyStmtClass:
   case Stmt::CoreturnStmtClass:
+  case Stmt::CXXExpansionStmtPatternClass:
+  case Stmt::CXXExpansionStmtInstantiationClass:
     K = CXCursor_UnexposedStmt;
     break;
 
@@ -345,6 +347,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl 
*Parent,
   case Stmt::EmbedExprClass:
   case Stmt::HLSLOutArgExprClass:
   case Stmt::OpenACCAsteriskSizeExprClass:
+  case Stmt::CXXExpansionSelectExprClass:
     K = CXCursor_UnexposedExpr;
     break;
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to