[clang] [Clang] [C++26] Expansion Statements (P1306R5) (PR #169680)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 10 13:49:33 PDT 2026


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

>From 5db185c78b21bd8f3aef98ab2aaaed3db554bf16 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 25 Nov 2025 17:18:05 +0100
Subject: [PATCH 1/5] [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             | 583 ++++++++++++++++++
 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                 | 119 ++++
 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                     | 156 +++++
 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     |  46 ++
 clang/lib/Serialization/ASTWriterDecl.cpp     |   9 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |  32 +
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |   3 +
 clang/tools/libclang/CIndex.cpp               |   1 +
 clang/tools/libclang/CXCursor.cpp             |   3 +
 47 files changed, 1438 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..cf44268c6e695 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,588 @@ 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 'preamble 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.
+///   <preamble 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;
+
+  CXXExpansionStmtDecl *Parent;
+
+  // Instantiations are stored first, then preamble statements.
+  const unsigned NumInstantiations : 20;
+  const unsigned NumPreambleStmts : 3;
+  unsigned ShouldApplyLifetimeExtensionToPreamble : 1;
+
+  CXXExpansionStmtInstantiation(EmptyShell Empty, unsigned NumInstantiations,
+                                unsigned NumPreambleStmts);
+  CXXExpansionStmtInstantiation(CXXExpansionStmtDecl *Parent,
+                                ArrayRef<Stmt *> Instantiations,
+                                ArrayRef<Stmt *> PreambleStmts,
+                                bool ShouldApplyLifetimeExtensionToPreamble);
+
+public:
+  static CXXExpansionStmtInstantiation *
+  Create(ASTContext &C, CXXExpansionStmtDecl *Parent,
+         ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> PreambleStmts,
+         bool ShouldApplyLifetimeExtensionToPreamble);
+
+  static CXXExpansionStmtInstantiation *CreateEmpty(ASTContext &C,
+                                                    EmptyShell Empty,
+                                                    unsigned NumInstantiations,
+                                                    unsigned NumPreambleStmts);
+
+  ArrayRef<Stmt *> getAllSubStmts() const {
+    return getTrailingObjects(getNumSubStmts());
+  }
+
+  MutableArrayRef<Stmt *> getAllSubStmts() {
+    return getTrailingObjects(getNumSubStmts());
+  }
+
+  unsigned getNumSubStmts() const { return NumInstantiations + NumPreambleStmts; }
+
+  ArrayRef<Stmt *> getInstantiations() const {
+    return getTrailingObjects(NumInstantiations);
+  }
+
+  ArrayRef<Stmt *> getPreambleStmts() const {
+    return getAllSubStmts().drop_front(NumInstantiations);
+  }
+
+  bool shouldApplyLifetimeExtensionToPreamble() const {
+    return ShouldApplyLifetimeExtensionToPreamble;
+  }
+
+  void setShouldApplyLifetimeExtensionToPreamble(bool Apply) {
+    ShouldApplyLifetimeExtensionToPreamble = Apply;
+  }
+
+  SourceLocation getBeginLoc() const {
+    return Parent->getExpansionPattern()->getBeginLoc();
+  }
+
+  SourceLocation getEndLoc() const {
+    return Parent->getExpansionPattern()->getEndLoc();
+  }
+
+  CXXExpansionStmtDecl *getParent() { return Parent; }
+  const CXXExpansionStmtDecl *getParent() const { return Parent; }
+
+  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..a85e6d1e1bb15 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,80 @@ 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 ToParent = importChecked(Err, S->getParent());
+  for (Stmt *FromInst : S->getInstantiations())
+    ToInstantiations.push_back(importChecked(Err, FromInst));
+  for (Stmt *FromShared : S->getPreambleStmts())
+    ToSharedStmts.push_back(importChecked(Err, FromShared));
+
+  if (Err)
+    return std::move(Err);
+
+  return CXXExpansionStmtInstantiation::Create(
+      Importer.getToContext(), ToParent, ToInstantiations, ToSharedStmts,
+      S->shouldApplyLifetimeExtensionToPreamble());
+}
+
 ExpectedStmt
 ASTNodeImporter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
   Error Err = Error::success();
@@ -9359,6 +9466,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..3ac68ca04a5ef 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,158 @@ 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 NumPreambleStmts)
+    : Stmt(CXXExpansionStmtInstantiationClass, Empty),
+      NumInstantiations(NumInstantiations), NumPreambleStmts(NumPreambleStmts) {
+  assert(NumPreambleStmts <= 4 && "might have to allocate more bits for this");
+}
+
+CXXExpansionStmtInstantiation::CXXExpansionStmtInstantiation(
+    CXXExpansionStmtDecl *Parent, ArrayRef<Stmt *> Instantiations,
+    ArrayRef<Stmt *> PreambleStmts, bool ShouldApplyLifetimeExtensionToPreamble)
+    : Stmt(CXXExpansionStmtInstantiationClass), Parent(Parent),
+      NumInstantiations(unsigned(Instantiations.size())),
+      NumPreambleStmts(unsigned(PreambleStmts.size())),
+      ShouldApplyLifetimeExtensionToPreamble(
+          ShouldApplyLifetimeExtensionToPreamble) {
+  assert(NumPreambleStmts <= 4 && "might have to allocate more bits for this");
+  llvm::uninitialized_copy(Instantiations, getTrailingObjects());
+  llvm::uninitialized_copy(PreambleStmts,
+                           getTrailingObjects() + NumInstantiations);
+}
+
+CXXExpansionStmtInstantiation *CXXExpansionStmtInstantiation::Create(
+    ASTContext &C, CXXExpansionStmtDecl *Parent,
+    ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> PreambleStmts,
+    bool ShouldApplyLifetimeExtensionToPreamble) {
+  void *Mem = C.Allocate(
+      totalSizeToAlloc<Stmt *>(Instantiations.size() + PreambleStmts.size()),
+      alignof(CXXExpansionStmtInstantiation));
+  return new (Mem)
+      CXXExpansionStmtInstantiation(Parent, Instantiations, PreambleStmts,
+                                    ShouldApplyLifetimeExtensionToPreamble);
+}
+
+CXXExpansionStmtInstantiation *
+CXXExpansionStmtInstantiation::CreateEmpty(ASTContext &C, EmptyShell Empty,
+                                           unsigned NumInstantiations,
+                                           unsigned NumPreambleStmts) {
+  void *Mem =
+      C.Allocate(totalSizeToAlloc<Stmt *>(NumInstantiations + NumPreambleStmts),
+                 alignof(CXXExpansionStmtInstantiation));
+  return new (Mem)
+      CXXExpansionStmtInstantiation(Empty, NumInstantiations, NumPreambleStmts);
+}
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..d5c08764d8e80 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->shouldApplyLifetimeExtensionToPreamble());
+}
+
 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..0c3c47153d958 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->shouldApplyLifetimeExtensionToPreamble())
+    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..41a35dbf72f15 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1775,6 +1775,34 @@ 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->Parent = cast<CXXExpansionStmtDecl>(Record.readDeclRef());
+  for (unsigned I = 0; I < S->getNumSubStmts(); ++I)
+    S->getAllSubStmts()[I] = Record.readSubStmt();
+  S->setShouldApplyLifetimeExtensionToPreamble(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 +3651,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 +4541,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..da62a6a31c6dc 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1757,6 +1757,38 @@ 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->getPreambleStmts().size());
+  Record.AddDeclRef(S->getParent());
+  for (Stmt *St : S->getAllSubStmts())
+    Record.AddStmt(St);
+  Record.push_back(S->shouldApplyLifetimeExtensionToPreamble());
+  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;
 

>From 8fa0c738926406a4b1ecce04e759b290fc1f6cd5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 10 Jul 2026 22:04:34 +0200
Subject: [PATCH 2/5] [Clang] [C++26] Expansion Statements (Part 2: Parsing and
 Parser Tests) (#169681)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This patch implements parsing of expansion statements and expansion
initialiser lists. I’ve also had to implement sema for
`CXXExpansionStmtDecl` because parsing doesn’t really work without that
node.
---
 clang/docs/ReleaseNotes.rst                   |    2 +
 .../clang/Basic/DiagnosticParseKinds.td       |   11 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   27 +-
 clang/include/clang/Parse/Parser.h            |   37 +-
 clang/include/clang/Sema/ScopeInfo.h          |    9 +-
 clang/include/clang/Sema/Sema.h               |  105 +-
 clang/lib/AST/ByteCode/Compiler.cpp           |   42 +
 clang/lib/AST/ByteCode/Compiler.h             |    3 +-
 clang/lib/AST/ExprConstant.cpp                |   40 +
 clang/lib/CodeGen/CGDecl.cpp                  |    8 +-
 clang/lib/CodeGen/CGStmt.cpp                  |   29 +-
 clang/lib/CodeGen/CodeGenFunction.h           |    3 +
 clang/lib/Frontend/FrontendActions.cpp        |    2 +
 clang/lib/Parse/ParseDecl.cpp                 |   59 +-
 clang/lib/Parse/ParseExpr.cpp                 |   14 +-
 clang/lib/Parse/ParseInit.cpp                 |   19 +
 clang/lib/Parse/ParseStmt.cpp                 |  195 +-
 clang/lib/Sema/CMakeLists.txt                 |    1 +
 clang/lib/Sema/SemaDecl.cpp                   |   10 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |    3 +
 clang/lib/Sema/SemaExpand.cpp                 |  641 +++++++
 clang/lib/Sema/SemaLookup.cpp                 |   47 +-
 clang/lib/Sema/SemaStmt.cpp                   |  542 +++---
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |   29 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   38 +-
 clang/lib/Sema/SemaTemplateVariadic.cpp       |    8 +-
 clang/lib/Sema/TreeTransform.h                |  190 +-
 clang/test/AST/ast-dump-expansion-stmt.cpp    |   54 +
 clang/test/AST/ast-print-expansion-stmts.cpp  |  113 ++
 .../cxx2c-destructuring-expansion-stmt.cpp    |  532 ++++++
 ...cxx2c-enumerating-expansion-statements.cpp | 1518 ++++++++++++++++
 .../cxx2c-expansion-stmts-control-flow.cpp    |  430 +++++
 .../cxx2c-expansion-stmts-mangling.cpp        |  134 ++
 .../cxx2c-expansion-stmts-templates.cpp       |  208 +++
 .../cxx2c-iterating-expansion-stmt.cpp        |  551 ++++++
 ...2c-expansion-statements-not-backported.cpp |    5 +
 .../Parser/cxx2c-expansion-statements.cpp     |   68 +
 .../cxx2c-expansion-statements-shadow.cpp     |   11 +
 .../cxx2c-expansion-stmts-control-flow.cpp    |  135 ++
 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp  | 1590 +++++++++++++++++
 clang/test/SemaTemplate/GH176155.cpp          |   20 +-
 clang/www/cxx_status.html                     |    8 +-
 42 files changed, 7157 insertions(+), 334 deletions(-)
 create mode 100644 clang/lib/Sema/SemaExpand.cpp
 create mode 100644 clang/test/AST/ast-dump-expansion-stmt.cpp
 create mode 100644 clang/test/AST/ast-print-expansion-stmts.cpp
 create mode 100644 clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp
 create mode 100644 clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp
 create mode 100644 clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp
 create mode 100644 clang/test/CodeGenCXX/cxx2c-expansion-stmts-mangling.cpp
 create mode 100644 clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp
 create mode 100644 clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp
 create mode 100644 clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp
 create mode 100644 clang/test/Parser/cxx2c-expansion-statements.cpp
 create mode 100644 clang/test/SemaCXX/cxx2c-expansion-statements-shadow.cpp
 create mode 100644 clang/test/SemaCXX/cxx2c-expansion-stmts-control-flow.cpp
 create mode 100644 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index fc17eead02589..1fab855bb1fc2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -126,6 +126,8 @@ C++ Language Changes
 
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
+- Clang now has partial support for `P1306R5 <https://wg21.link/P1306R5>`_ Expansion Statements. Iterating expansion
+  statements currently cannot be expanded and will result in a diagnostic, but other types of expansion statements work.
 
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 7bcd1870a2600..b0f9819397842 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -421,9 +421,10 @@ def warn_cxx98_compat_for_range : Warning<
   "range-based for loop is incompatible with C++98">,
   InGroup<CXX98Compat>, DefaultIgnore;
 def err_for_range_identifier : Error<
-  "range-based for loop requires type for loop variable">;
+  "%select{range-based for loop|expansion statement}0 requires "
+  "type for %select{loop|expansion}0 variable">;
 def err_for_range_expected_decl : Error<
-  "for range declaration must declare a variable">;
+  "%select{for range|expansion statement}0 declaration must declare a variable">;
 def err_argument_required_after_attribute : Error<
   "argument required after attribute">;
 def err_missing_param : Error<"expected parameter declarator">;
@@ -450,6 +451,12 @@ def err_unspecified_size_with_static : Error<
   "'static' may not be used without an array size">;
 def err_expected_parentheses_around_typename : Error<
   "expected parentheses around type name in %0 expression">;
+def err_expansion_stmt_requires_range : Error<
+  "expansion statement must use the syntax of a range-based for loop">;
+def err_expansion_stmt_requires_cxx2c : Error<
+  "expansion statements are only supported in C++2c">;
+def err_for_template : Error<
+  "'for template' is invalid; use 'template for' instead">;
 
 def err_expected_case_before_expression: Error<
   "expected 'case' keyword before expression">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4d352f1def04b..bfe0fe75389fa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -165,6 +165,10 @@ def err_ice_too_large : Error<
 def err_expr_not_string_literal : Error<"expression is not a string literal">;
 def note_constexpr_assert_failed : Note<
   "assertion failed during evaluation of constant expression">;
+def err_expansion_size_expr_not_ice : Error<
+  "expansion statement size is not a constant expression">;
+def err_iterating_expansion_stmt_unsupported : Error<
+  "iterating expansion statements are not yet supported">;
 
 // Semantic analysis of constant literals.
 def ext_predef_outside_function : Warning<
@@ -2926,10 +2930,10 @@ def note_which_delegates_to : Note<"which delegates to">;
 
 // C++11 range-based for loop
 def err_for_range_decl_must_be_var : Error<
-  "for range declaration must declare a variable">;
+  "%select{for range|expansion statement}0 declaration must declare a variable">;
 def err_for_range_storage_class : Error<
-  "loop variable %0 may not be declared %select{'extern'|'static'|"
-  "'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}1">;
+  "%select{loop|expansion}0 variable %1 may not be declared %select{'extern'|'static'|"
+  "'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}2">;
 def err_type_defined_in_for_range : Error<
   "types may not be defined in a for range declaration">;
 def err_for_range_deduction_failure : Error<
@@ -3734,6 +3738,21 @@ def err_conflicting_codeseg_attribute : Error<
 def warn_duplicate_codeseg_attribute : Warning<
   "duplicate code segment specifiers">, InGroup<Section>;
 
+def err_expansion_stmt_invalid_init : Error<
+  "cannot expand expression of type %0">;
+def err_expansion_stmt_vla : Error<
+  "cannot expand variable length array type %0">;
+def err_expansion_stmt_incomplete : Error<
+  "cannot expand expression of incomplete type %0">;
+def err_expansion_stmt_lambda : Error<
+  "cannot expand lambda closure type">;
+def err_expansion_stmt_case : Error<
+  "%select{'case'|'default'}0 belongs to 'switch' outside enclosing expansion statement">;
+def note_enclosing_switch_statement_here : Note<
+  "switch statement is here">;
+def err_expansion_stmt_label : Error<
+  "labels are not allowed in expansion statements">;
+
 def err_attribute_patchable_function_entry_invalid_section
     : Error<"section argument to 'patchable_function_entry' attribute is not "
             "valid for this target: %0">;
@@ -5954,6 +5973,8 @@ def note_template_nsdmi_here : Note<
   "in instantiation of default member initializer %q0 requested here">;
 def note_template_type_alias_instantiation_here : Note<
   "in instantiation of template type alias %0 requested here">;
+def note_expansion_stmt_instantiation_here : Note<
+  "in instantiation of expansion statement requested here">;
 def note_template_exception_spec_instantiation_here : Note<
   "in instantiation of exception specification for %0 requested here">;
 def note_template_requirement_instantiation_here : Note<
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 08a3d88ee6a36..4d23f62a44646 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1718,6 +1718,7 @@ class Parser : public CodeCompletionHandler {
     SourceLocation ColonLoc;
     ExprResult RangeExpr;
     SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
+    CXXExpansionStmtDecl* ExpansionStmt = nullptr;
     bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
   };
   struct ForRangeInfo : ForRangeInit {
@@ -4196,7 +4197,8 @@ class Parser : public CodeCompletionHandler {
   bool ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
                            llvm::function_ref<void()> ExpressionStarts =
                                llvm::function_ref<void()>(),
-                           bool FailImmediatelyOnInvalidExpr = false);
+                           bool FailImmediatelyOnInvalidExpr = false,
+                           bool ParsingExpansionStmtInitList = false);
 
   /// ParseSimpleExpressionList - A simple comma-separated list of expressions,
   /// used for misc language extensions.
@@ -5289,6 +5291,16 @@ class Parser : public CodeCompletionHandler {
   ///
   ExprResult ParseBraceInitializer();
 
+  /// ParseExpansionInitList - Called when the initializer of an expansion
+  /// statement starts with an open brace.
+  ///
+  /// \verbatim
+  ///       expansion-init-list: [C++26 [stmt.expand]]
+  ///          '{' expression-list ','[opt] '}'
+  ///          '{' '}'
+  /// \endverbatim
+  ExprResult ParseExpansionInitList();
+
   struct DesignatorCompletionInfo {
     SmallVectorImpl<Expr *> &InitExprs;
     QualType PreferredBaseType;
@@ -7503,7 +7515,11 @@ class Parser : public CodeCompletionHandler {
   /// [C++0x]   braced-init-list            [TODO]
   /// \endverbatim
   StmtResult ParseForStatement(SourceLocation *TrailingElseLoc,
-                               LabelDecl *PrecedingLabel);
+                               LabelDecl *PrecedingLabel,
+                               CXXExpansionStmtDecl *ESD = nullptr);
+
+  void ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
+                                          ParsingDeclSpec *VarDeclSpec);
 
   /// ParseGotoStatement
   /// \verbatim
@@ -7560,6 +7576,23 @@ class Parser : public CodeCompletionHandler {
   /// \endverbatim
   StmtResult ParseDeferStatement(SourceLocation *TrailingElseLoc);
 
+  /// ParseExpansionStatement - Parse a C++26 expansion
+  /// statement ('template for').
+  ///
+  /// \verbatim
+  ///     expansion-statement:
+  ///       'template' 'for' '(' init-statement[opt]
+  ///           for-range-declaration ':' expansion-initializer ')'
+  ///           compound-statement
+  ///
+  ///     expansion-initializer:
+  ///       expression
+  ///       expansion-init-list
+  /// \endverbatim
+  StmtResult ParseExpansionStatement(SourceLocation *TrailingElseLoc,
+                                     LabelDecl *PrecedingLabel,
+                                     SourceLocation TemplateLoc);
+
   StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext StmtCtx,
                                  SourceLocation *TrailingElseLoc,
                                  ParsedAttributes &Attrs,
diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index f334f58ebd0a7..a0753fa333770 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -201,8 +201,13 @@ class FunctionScopeInfo {
 
 public:
   /// A SwitchStmt, along with a flag indicating if its list of case statements
-  /// is incomplete (because we dropped an invalid one while parsing).
-  using SwitchInfo = llvm::PointerIntPair<SwitchStmt*, 1, bool>;
+  /// is incomplete (because we dropped an invalid one while parsing), as well
+  /// as the DeclContext containing the statement.
+  struct SwitchInfo : llvm::PointerIntPair<SwitchStmt *, 1, bool> {
+    DeclContext *EnclosingDC;
+    SwitchInfo(SwitchStmt *Switch, DeclContext *DC)
+        : PointerIntPair(Switch, false), EnclosingDC(DC) {}
+  };
 
   /// SwitchStack - This is the current set of active switch statements in the
   /// block.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1b8a9803be472..d41cf7cdbcc00 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -907,6 +907,7 @@ class Sema final : public SemaBase {
   // 33. Types (SemaType.cpp)
   // 34. FixIt Helpers (SemaFixItUtils.cpp)
   // 35. Function Effects (SemaFunctionEffects.cpp)
+  // 36. C++ Expansion Statements (SemaExpand.cpp)
 
   /// \name Semantic Analysis
   /// Implementations are in Sema.cpp
@@ -4164,7 +4165,7 @@ class Sema final : public SemaBase {
   /// complete.
   void ActOnInitializerError(Decl *Dcl);
 
-  void ActOnCXXForRangeDecl(Decl *D);
+  void ActOnCXXForRangeDecl(Decl *D, bool InExpansionStmt);
   StmtResult ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
                                         IdentifierInfo *Ident,
                                         ParsedAttributes &Attrs);
@@ -9609,9 +9610,11 @@ class Sema final : public SemaBase {
   /// LookupOrCreateLabel - Do a name lookup of a label with the specified name.
   /// If GnuLabelLoc is a valid source location, then this is a definition
   /// of an __label__ label name, otherwise it is a normal label definition
-  /// or use.
+  /// or use. If IsLabelStmt is true, then this is the label of a
+  /// labeled-statement.
   LabelDecl *LookupOrCreateLabel(IdentifierInfo *II, SourceLocation IdentLoc,
-                                 SourceLocation GnuLabelLoc = SourceLocation());
+                                 SourceLocation GnuLabelLoc = SourceLocation(),
+                                 bool IsLabelStmt = false);
 
   /// Perform a name lookup for a label with the specified name; this does not
   /// create a new label if the lookup fails.
@@ -11177,6 +11180,43 @@ class Sema final : public SemaBase {
       BuildForRangeKind Kind,
       ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
 
+  /// Set the type of a for-range declaration whose for-range or expansion
+  /// initialiser is dependent.
+  void ActOnDependentForRangeInitializer(VarDecl *LoopVar,
+                                         BuildForRangeKind BFRK);
+
+  /// Holds the 'begin' and 'end' variables of a range-based for loop or
+  /// expansion statement; begin-expr and end-expr are also provided; the
+  /// latter are used in some diagnostics.
+  struct ForRangeBeginEndInfo {
+    VarDecl *BeginVar = nullptr;
+    VarDecl *EndVar = nullptr;
+    Expr *BeginExpr = nullptr;
+    Expr *EndExpr = nullptr;
+    bool isValid() const { return BeginVar != nullptr && EndVar != nullptr; }
+  };
+
+  /// Determine begin-expr and end-expr and build variable declarations for
+  /// them as per [stmt.ranged].
+  ForRangeBeginEndInfo BuildCXXForRangeBeginEndVars(
+      Scope *S, VarDecl *RangeVar, SourceLocation ColonLoc,
+      SourceLocation CoawaitLoc,
+      ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps,
+      BuildForRangeKind Kind, bool IsConstexpr,
+      StmtResult *RebuildResult = nullptr,
+      llvm::function_ref<StmtResult()> RebuildWithDereference = {},
+      IdentifierInfo *BeginName = nullptr, IdentifierInfo *EndName = nullptr);
+
+  /// Helper used by the expansion statements and for-range code to build
+  /// a variable declaration for e.g. 'begin' and 'end'.
+  VarDecl *BuildForRangeVarDecl(SourceLocation Loc, QualType Type,
+                                IdentifierInfo *Name, bool IsConstexpr);
+
+  /// Build the range variable of a range-based for loop or iterating
+  /// expansion statement and return its DeclStmt.
+  StmtResult BuildCXXForRangeRangeVar(Scope *S, Expr *Range, QualType Type,
+                                      bool IsConstexpr = false);
+
   /// FinishCXXForRangeStmt - Attach the body to a C++0x for-range statement.
   /// This is a separate step from ActOnCXXForRangeStmt because analysis of the
   /// body cannot be performed until after the type of the range variable is
@@ -11322,6 +11362,9 @@ class Sema final : public SemaBase {
                                            SourceLocation Loc,
                                            unsigned NumParams);
 
+  void ApplyForRangeOrExpansionStatementLifetimeExtension(
+      VarDecl *RangeVar, ArrayRef<MaterializeTemporaryExpr *> Temporaries);
+
 private:
   /// Check whether the given statement can have musttail applied to it,
   /// issuing a diagnostic and returning false if not.
@@ -13309,6 +13352,9 @@ class Sema final : public SemaBase {
       /// We are performing overload resolution for a call to a function
       /// template or variable template named 'sycl_kernel_launch'.
       SYCLKernelLaunchOverloadResolution,
+
+      /// We are instantiating an expansion statement.
+      ExpansionStmtInstantiation,
     } Kind;
 
     /// Whether we're substituting into constraints.
@@ -13504,6 +13550,12 @@ class Sema final : public SemaBase {
                           concepts::Requirement *Req,
                           SourceRange InstantiationRange = SourceRange());
 
+    /// \brief Note that we are substituting the body of an expansion statement.
+    InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+                          CXXExpansionStmtPattern *ExpansionStmt,
+                          ArrayRef<TemplateArgument> TArgs,
+                          SourceRange InstantiationRange);
+
     /// \brief Note that we are checking the satisfaction of the constraint
     /// expression inside of a nested requirement.
     InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
@@ -15795,6 +15847,53 @@ class Sema final : public SemaBase {
   void performFunctionEffectAnalysis(TranslationUnitDecl *TU);
 
   ///@}
+
+  //
+  //
+  // -------------------------------------------------------------------------
+  //
+  //
+
+  /// \name Expansion Statements
+  /// Implementations are in SemaExpand.cpp
+  ///@{
+public:
+  CXXExpansionStmtDecl *ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
+                                                  SourceLocation TemplateKWLoc);
+
+  CXXExpansionStmtDecl *
+  BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc,
+                            NonTypeTemplateParmDecl *NTTP);
+
+  ExprResult ActOnCXXExpansionInitList(MultiExprArg SubExprs,
+                                       SourceLocation LBraceLoc,
+                                       SourceLocation RBraceLoc);
+
+  StmtResult ActOnCXXExpansionStmtPattern(
+      CXXExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt,
+      Expr *ExpansionInitializer, SourceLocation LParenLoc,
+      SourceLocation ColonLoc, SourceLocation RParenLoc,
+      ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps);
+
+  StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body);
+
+  StmtResult BuildCXXEnumeratingExpansionStmtPattern(Decl *ESD, Stmt *Init,
+                                                     Stmt *ExpansionVar,
+                                                     SourceLocation LParenLoc,
+                                                     SourceLocation ColonLoc,
+                                                     SourceLocation RParenLoc);
+
+  StmtResult BuildNonEnumeratingCXXExpansionStmtPattern(
+      CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt,
+      Expr *ExpansionInitializer, SourceLocation LParenLoc,
+      SourceLocation ColonLoc, SourceLocation RParenLoc,
+      ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
+
+  ExprResult BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx);
+
+  std::optional<uint64_t>
+  ComputeExpansionSize(CXXExpansionStmtPattern *Expansion);
+  ///@}
 };
 
 DeductionFailureInfo
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index c7f074c9efc6a..0215db156e516 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5920,6 +5920,9 @@ template <class Emitter> bool Compiler<Emitter>::visitStmt(const Stmt *S) {
     return this->emitInvalid(S);
   case Stmt::LabelStmtClass:
     return this->visitStmt(cast<LabelStmt>(S)->getSubStmt());
+  case Stmt::CXXExpansionStmtInstantiationClass:
+    return this->visitCXXExpansionStmtInstantiation(
+        cast<CXXExpansionStmtInstantiation>(S));
   default: {
     if (const auto *E = dyn_cast<Expr>(S))
       return this->discard(E);
@@ -6004,6 +6007,13 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
             FunctionDecl, NamespaceAliasDecl, UsingDirectiveDecl>(D))
       continue;
 
+    if (const auto *ESD = dyn_cast<CXXExpansionStmtDecl>(D)) {
+      assert(ESD->getInstantiations() && "not expanded?");
+      if (!this->visitStmt(ESD->getInstantiations()))
+        return false;
+      continue;
+    }
+
     const auto *VD = dyn_cast<VarDecl>(D);
     if (!VD)
       return false;
@@ -6590,6 +6600,38 @@ bool Compiler<Emitter>::visitCXXTryStmt(const CXXTryStmt *S) {
   return this->visitStmt(S->getTryBlock());
 }
 
+/// template for (auto x : {1, 2}) {}
+///
+/// This is not a loop from an AST perspective at all since it has already
+/// been instantiated to a list of compound statements.
+///
+/// Since we can have control flow in those compound statements, we need to
+/// handle it mostly like a loop though.
+template <class Emitter>
+bool Compiler<Emitter>::visitCXXExpansionStmtInstantiation(
+    const CXXExpansionStmtInstantiation *S) {
+  LocalScope<Emitter> WholeLoopScope(this, ScopeKind::Block);
+
+  for (const Stmt *PreambleStmt : S->getPreambleStmts()) {
+    if (!this->visitDeclStmt(cast<DeclStmt>(PreambleStmt), true))
+      return false;
+  }
+
+  LabelTy EndLabel = this->getLabel();
+  for (const Stmt *Instantiation : S->getInstantiations()) {
+    LabelTy ContinueLabel = this->getLabel();
+    LoopScope<Emitter> LS(this, S, EndLabel, ContinueLabel);
+
+    if (!this->visitStmt(Instantiation))
+      return false;
+    this->emitLabel(ContinueLabel);
+  }
+
+  this->emitLabel(EndLabel);
+
+  return WholeLoopScope.destroyLocals();
+}
+
 template <class Emitter>
 bool Compiler<Emitter>::emitLambdaStaticInvokerBody(const CXXMethodDecl *MD) {
   assert(MD->isLambdaStaticInvoker());
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index cd14f72b87f9f..551b07f9d8db0 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -249,7 +249,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
   bool visitDefaultStmt(const DefaultStmt *S);
   bool visitAttributedStmt(const AttributedStmt *S);
   bool visitCXXTryStmt(const CXXTryStmt *S);
-
+  bool
+  visitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *S);
 protected:
   bool visitStmt(const Stmt *S);
   bool visitExpr(const Expr *E, bool DestroyToplevelScope) override;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index a464ed90f70b8..f9030cbd77515 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5968,6 +5968,12 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
       const VarDecl *VD = dyn_cast_or_null<VarDecl>(D);
       if (VD && !CheckLocalVariableDeclaration(Info, VD))
         return ESR_Failed;
+
+      if (const auto *ESD = dyn_cast<CXXExpansionStmtDecl>(D)) {
+        assert(ESD->getInstantiations() && "not expanded?");
+        return EvaluateStmt(Result, Info, ESD->getInstantiations(), Case);
+      }
+
       // Each declaration initialization is its own full-expression.
       FullExpressionRAII Scope(Info);
       if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) &&
@@ -6240,6 +6246,40 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
     return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
   }
 
+  case Stmt::CXXExpansionStmtInstantiationClass: {
+    BlockScopeRAII Scope(Info);
+    const auto *Expansion = cast<CXXExpansionStmtInstantiation>(S);
+    for (const Stmt *PreambleStmt : Expansion->getPreambleStmts()) {
+      EvalStmtResult ESR = EvaluateStmt(Result, Info, PreambleStmt);
+      if (ESR != ESR_Succeeded) {
+        if (ESR != ESR_Failed && !Scope.destroy())
+          return ESR_Failed;
+        return ESR;
+      }
+    }
+
+    // No need to push an extra scope for these since they're already
+    // CompoundStmts.
+    EvalStmtResult ESR = ESR_Succeeded;
+    for (const Stmt *Instantiation : Expansion->getInstantiations()) {
+      ESR = EvaluateStmt(Result, Info, Instantiation);
+      if (ESR == ESR_Failed ||
+          ShouldPropagateBreakContinue(Info, Expansion, &Scope, ESR))
+        return ESR;
+      if (ESR != ESR_Continue) {
+        // Succeeded here actually means we encountered a 'break'.
+        assert(ESR == ESR_Succeeded || ESR == ESR_Returned);
+        break;
+      }
+    }
+
+    // Map Continue back to Succeeded if we fell off the end of the loop.
+    if (ESR == ESR_Continue)
+      ESR = ESR_Succeeded;
+
+    return Scope.destroy() ? ESR : ESR_Failed;
+  }
+
   case Stmt::SwitchStmtClass:
     return EvaluateSwitch(Result, Info, cast<SwitchStmt>(S));
 
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 047aa55dc4b4a..de6b751c155ff 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -143,8 +143,12 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
     // None of these decls require codegen support.
     return;
 
-  case Decl::CXXExpansionStmt:
-    llvm_unreachable("TODO");
+  case Decl::CXXExpansionStmt: {
+    const auto *ESD = cast<CXXExpansionStmtDecl>(&D);
+    assert(ESD->getInstantiations() && "expansion statement not expanded?");
+    EmitStmt(ESD->getInstantiations());
+    return;
+  }
 
   case Decl::NamespaceAlias:
     if (CGDebugInfo *DI = getDebugInfo())
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 8d9eb67cc2cef..9e0ea7d5eb880 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -206,7 +206,8 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
   case Stmt::CXXExpansionStmtPatternClass:
     llvm_unreachable("unexpanded expansion statements should not be emitted");
   case Stmt::CXXExpansionStmtInstantiationClass:
-    llvm_unreachable("Todo");
+    EmitCXXExpansionStmtInstantiation(cast<CXXExpansionStmtInstantiation>(*S));
+    break;
   case Stmt::SEHTryStmtClass:
     EmitSEHTryStmt(cast<SEHTryStmt>(*S));
     break;
@@ -1511,6 +1512,32 @@ CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S,
   }
 }
 
+void CodeGenFunction::EmitCXXExpansionStmtInstantiation(
+    const CXXExpansionStmtInstantiation &S) {
+  LexicalScope Scope(*this, S.getSourceRange());
+
+  for (const Stmt *DS : S.getPreambleStmts())
+    EmitStmt(DS);
+
+  if (S.getInstantiations().empty())
+    return;
+
+  JumpDest ExpandExit = getJumpDestInCurrentScope("expand.end");
+  JumpDest ContinueDest;
+  for (auto [N, Inst] : enumerate(S.getInstantiations())) {
+    if (N == S.getInstantiations().size() - 1)
+      ContinueDest = ExpandExit;
+    else
+      ContinueDest = getJumpDestInCurrentScope("expand.next");
+
+    LexicalScope ExpansionScope(*this, Inst->getSourceRange());
+    BreakContinueStack.push_back(BreakContinue(S, ExpandExit, ContinueDest));
+    EmitStmt(Inst);
+    BreakContinueStack.pop_back();
+    EmitBlock(ContinueDest.getBlock(), true);
+  }
+}
+
 void CodeGenFunction::EmitReturnOfRValue(RValue RV, QualType Ty) {
   if (RV.isScalar()) {
     Builder.CreateStore(RV.getScalarVal(), ReturnValue);
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 0ff93d2ce7363..b045edb83b568 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3734,6 +3734,9 @@ class CodeGenFunction : public CodeGenTypeCache {
   void EmitCXXForRangeStmt(const CXXForRangeStmt &S,
                            ArrayRef<const Attr *> Attrs = {});
 
+  void
+  EmitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation &S);
+
   /// Controls insertion of cancellation exit blocks in worksharing constructs.
   class OMPCancelStackRAII {
     CodeGenFunction &CGF;
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index e5eaab0da7adb..786aeba183ff6 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -478,6 +478,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
       return "SYCLKernelLaunchLookup";
     case CodeSynthesisContext::SYCLKernelLaunchOverloadResolution:
       return "SYCLKernelLaunchOverloadResolution";
+    case CodeSynthesisContext::ExpansionStmtInstantiation:
+      return "ExpansionStmtInstantiation";
     }
     return "";
   }
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index d448cb8a552bc..0ab6f10ed6ca6 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -33,6 +33,7 @@
 #include "clang/Sema/SemaOpenMP.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/ScopeExit.h"
 #include <optional>
 
 using namespace clang;
@@ -2303,43 +2304,39 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
   // Handle the Objective-C for-in loop variable similarly, although we
   // don't need to parse the container in advance.
   if (FRI && (Tok.is(tok::colon) || isTokIdentifier_in())) {
-    bool IsForRangeLoop = false;
+    // If we're parsing an expansion statement, enter its context to ensure
+    // the declaration ends up in a dependent context. We previously left the
+    // context of the expansion statement because init-statements should *not*
+    // be in a dependent context, and only once we get here do we know that this
+    // is not an init-statement but rather a for-range-declaration.
+    //
+    // Note that this is *not* the case if we get here via ParseCXXCondition()
+    // since if that is called when we parse an expansion statement, we know for
+    // sure that it has to be the for-range-declaration.
+    //
+    // We can't use ContextRAII here since that pushes a new delayed diagnostics
+    // pool, which causes issues during declaration parsing.
+    bool EnterContext = FRI->ExpansionStmt &&
+                        Actions.CurContext == FRI->ExpansionStmt->getParent();
+    if (EnterContext)
+      Actions.PushDeclContext(nullptr, FRI->ExpansionStmt);
+    llvm::scope_exit RestoreDeclContext{[&] {
+      if (EnterContext)
+        Actions.PopDeclContext();
+    }};
+
+    bool IsForRangeLoopOrExpansionStmt = false;
     if (TryConsumeToken(tok::colon, FRI->ColonLoc)) {
-      IsForRangeLoop = true;
-      EnterExpressionEvaluationContext ForRangeInitContext(
-          Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
-          /*LambdaContextDecl=*/nullptr,
-          Sema::ExpressionEvaluationContextRecord::EK_Other,
-          getLangOpts().CPlusPlus23);
-
-      // P2718R0 - Lifetime extension in range-based for loops.
-      if (getLangOpts().CPlusPlus23) {
-        auto &LastRecord = Actions.currentEvaluationContext();
-        LastRecord.InLifetimeExtendingContext = true;
-        LastRecord.RebuildDefaultArgOrDefaultInit = true;
-      }
-
-      if (getLangOpts().OpenMP)
+      IsForRangeLoopOrExpansionStmt = true;
+      if (getLangOpts().OpenMP && !FRI->ExpansionStmt)
         Actions.OpenMP().startOpenMPCXXRangeFor();
-      if (Tok.is(tok::l_brace))
-        FRI->RangeExpr = ParseBraceInitializer();
-      else
-        FRI->RangeExpr = ParseExpression();
-
-      // Before c++23, ForRangeLifetimeExtendTemps should be empty.
-      assert(
-          getLangOpts().CPlusPlus23 ||
-          Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
 
-      // Move the collected materialized temporaries into ForRangeInit before
-      // ForRangeInitContext exit.
-      FRI->LifetimeExtendTemps = std::move(
-          Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
+      ParseForRangeInitializerAfterColon(*FRI, &DS);
     }
 
     Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
-    if (IsForRangeLoop) {
-      Actions.ActOnCXXForRangeDecl(ThisDecl);
+    if (IsForRangeLoopOrExpansionStmt) {
+      Actions.ActOnCXXForRangeDecl(ThisDecl, FRI->ExpansionStmt);
     } else {
       // Obj-C for loop
       if (auto *VD = dyn_cast_or_null<VarDecl>(ThisDecl))
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 11d06b73d27ea..cf593c11d6690 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -3200,7 +3200,8 @@ void Parser::injectEmbedTokens() {
 
 bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
                                  llvm::function_ref<void()> ExpressionStarts,
-                                 bool FailImmediatelyOnInvalidExpr) {
+                                 bool FailImmediatelyOnInvalidExpr,
+                                 bool ParsingExpansionStmtInitList) {
   bool SawError = false;
   while (true) {
     if (ExpressionStarts)
@@ -3229,7 +3230,11 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
       SawError = true;
       if (FailImmediatelyOnInvalidExpr)
         break;
-      SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
+
+      // We expect '}' rather than ')' at the end of an expansion-init-list.
+      SkipUntil(tok::comma,
+                ParsingExpansionStmtInitList ? tok::r_brace : tok::r_paren,
+                StopAtSemi | StopBeforeMatch);
     } else {
       Exprs.push_back(Expr.get());
     }
@@ -3239,6 +3244,11 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
     // Move to the next argument, remember where the comma was.
     Token Comma = Tok;
     ConsumeToken();
+
+    // CWG 3061: Trailing commas are allowed in expansion-init-lists.
+    if (ParsingExpansionStmtInitList && Tok.is(tok::r_brace))
+      break;
+
     checkPotentialAngleBracketDelimiter(Comma);
   }
   return SawError;
diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp
index 0e86c4c48d5e4..40d78b5d3d2a6 100644
--- a/clang/lib/Parse/ParseInit.cpp
+++ b/clang/lib/Parse/ParseInit.cpp
@@ -516,6 +516,25 @@ ExprResult Parser::ParseBraceInitializer() {
   return ExprError(); // an error occurred.
 }
 
+ExprResult Parser::ParseExpansionInitList() {
+  BalancedDelimiterTracker T(*this, tok::l_brace);
+  T.consumeOpen();
+
+  ExprVector InitExprs;
+
+  if (!Tok.is(tok::r_brace) &&
+      ParseExpressionList(InitExprs, /*ExpressionStarts=*/{},
+                          /*FailImmediatelyOnInvalidExpr=*/false,
+                          /*ParsingExpansionStmtInitList=*/true)) {
+    T.consumeClose();
+    return ExprError();
+  }
+
+  T.consumeClose();
+  return Actions.ActOnCXXExpansionInitList(InitExprs, T.getOpenLocation(),
+                                           T.getCloseLocation());
+}
+
 bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
                                                     bool &InitExprsOk) {
   bool trailingComma = false;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 1a45ed66950be..32cab6782f9e5 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -261,6 +261,22 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
   }
 
   case tok::kw_template: {
+    if (NextToken().is(tok::kw_for)) {
+      // Expansion statements are not backported for now.
+      if (!getLangOpts().CPlusPlus26) {
+        Diag(Tok.getLocation(), diag::err_expansion_stmt_requires_cxx2c);
+
+        // Trying to parse this as a regular 'for' statement instead yields
+        // better error recovery.
+        ConsumeToken();
+        return ParseForStatement(TrailingElseLoc, PrecedingLabel);
+      }
+
+      SourceLocation TemplateLoc = ConsumeToken();
+      return ParseExpansionStatement(TrailingElseLoc, PrecedingLabel,
+                                     TemplateLoc);
+    }
+
     SourceLocation DeclEnd;
     ParseTemplateDeclarationOrSpecialization(DeclaratorContext::Block, DeclEnd,
                                              getAccessSpecifierIfPresent());
@@ -291,6 +307,19 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
     SemiError = "do/while";
     break;
   case tok::kw_for:                 // C99 6.8.5.3: for-statement
+    // Correct 'for template' to 'template for'.
+    if (NextToken().is(tok::kw_template)) {
+      Diag(Tok.getLocation(), diag::err_for_template)
+          << FixItHint::CreateReplacement(
+                 SourceRange(Tok.getLocation(), NextToken().getEndLoc()),
+                 "template for");
+      Tok.setKind(tok::kw_template);
+      SourceLocation TemplateLoc = ConsumeToken();
+      Tok.setKind(tok::kw_for);
+      return ParseExpansionStatement(TrailingElseLoc, PrecedingLabel,
+                                     TemplateLoc);
+    }
+
     return ParseForStatement(TrailingElseLoc, PrecedingLabel);
 
   case tok::kw_goto:                // C99 6.8.6.1: goto-statement
@@ -709,8 +738,9 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
   // identifier ':' statement
   SourceLocation ColonLoc = ConsumeToken();
 
-  LabelDecl *LD = Actions.LookupOrCreateLabel(IdentTok.getIdentifierInfo(),
-                                              IdentTok.getLocation());
+  LabelDecl *LD = Actions.LookupOrCreateLabel(
+      IdentTok.getIdentifierInfo(), IdentTok.getLocation(), /*GnuLabelLoc=*/{},
+      /*IsLabelStmt=*/true);
 
   // Read label attributes, if present.
   StmtResult SubStmt;
@@ -754,6 +784,13 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
 
   DiagnoseLabelFollowedByDecl(*this, SubStmt.get());
 
+  // If a label cannot appear here, just return the underlying statement. We
+  // already diagnosed this as invalid in LookupOrCreateLabel() above.
+  if (!LD) {
+    Attrs.clear();
+    return SubStmt.get();
+  }
+
   Actions.ProcessDeclAttributeList(Actions.CurScope, LD, Attrs);
   Attrs.clear();
 
@@ -1895,8 +1932,56 @@ bool Parser::isForRangeIdentifier() {
   return false;
 }
 
-StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
-                                     LabelDecl *PrecedingLabel) {
+void Parser::ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
+                                                ParsingDeclSpec *VarDeclSpec) {
+  // Use an immediate function context if this is the initializer for a
+  // constexpr variable in an expansion statement.
+  auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
+  if (FRI.ExpansionStmt && VarDeclSpec && VarDeclSpec->hasConstexprSpecifier())
+    Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+
+  EnterExpressionEvaluationContext InitContext(
+      Actions, Ctx,
+      /*LambdaContextDecl=*/nullptr,
+      Sema::ExpressionEvaluationContextRecord::EK_Other,
+      getLangOpts().CPlusPlus23);
+
+  // P2718R0 - Lifetime extension in range-based for loops.
+  if (getLangOpts().CPlusPlus23) {
+    auto &LastRecord = Actions.currentEvaluationContext();
+    LastRecord.InLifetimeExtendingContext = true;
+    LastRecord.RebuildDefaultArgOrDefaultInit = true;
+  }
+
+  if (FRI.ExpansionStmt) {
+    // The expansion-initializer is not in a dependent context and should
+    // thus be parsed in the parent context of the expansion statement.
+    assert(Actions.CurContext->isExpansionStmt());
+    Sema::ContextRAII CtxGuard(Actions, Actions.CurContext->getParent(),
+                               /*NewThis=*/false);
+    FRI.RangeExpr =
+        Tok.is(tok::l_brace) ? ParseExpansionInitList() : ParseExpression();
+    FRI.RangeExpr = Actions.MaybeCreateExprWithCleanups(FRI.RangeExpr);
+  } else if (Tok.is(tok::l_brace)) {
+    FRI.RangeExpr = ParseBraceInitializer();
+  } else {
+    FRI.RangeExpr = ParseExpression();
+  }
+
+  // Before c++23, ForRangeLifetimeExtendTemps should be empty.
+  assert(getLangOpts().CPlusPlus23 ||
+         Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
+
+  // Move the collected materialized temporaries into ForRangeInit before
+  // ForRangeInitContext exit.
+  FRI.LifetimeExtendTemps =
+      std::move(Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
+}
+
+StmtResult
+Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
+                          LabelDecl *PrecedingLabel,
+                          CXXExpansionStmtDecl *ESD) {
   assert(Tok.is(tok::kw_for) && "Not a for stmt!");
   SourceLocation ForLoc = ConsumeToken();  // eat the 'for'.
 
@@ -1931,8 +2016,14 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
   unsigned ScopeFlags = 0;
   if (C99orCXXorObjC)
     ScopeFlags = Scope::DeclScope | Scope::ControlScope;
+  if (ESD)
+    ScopeFlags |= Scope::TemplateParamScope;
 
   ParseScope ForScope(this, ScopeFlags);
+  if (ESD)
+    // FIXME: Combine this with the scope flags above once this is actually a
+    // proper scope flag.
+    getCurScope()->setIsExpansionStmtScope();
 
   BalancedDelimiterTracker T(*this, tok::l_paren);
   T.consumeOpen();
@@ -1945,7 +2036,21 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
   ExprResult Collection;
   ForRangeInfo ForRangeInfo;
   FullExprArg ThirdPart(Actions);
+  ForRangeInfo.ExpansionStmt = ESD;
 
+  // RAII helper to enter a context if we're parsing an expansion statement.
+  //
+  // This is required because some parts of an expansion statement (e.g. the
+  // init-statement) are not in a dependent context and must thus be parsed in
+  // the parent context.
+  struct [[nodiscard]] ExpansionStmtContextRAII : Sema::ContextRAII {
+    ExpansionStmtContextRAII(Sema &S, struct ForRangeInfo &Info,
+                             DeclContext *Ctx)
+        : ContextRAII(S, Info.ExpansionStmt ? Ctx : S.CurContext,
+                      /*NewThis=*/false) {}
+  };
+
+  assert(!ESD || Actions.CurContext->isExpansionStmt());
   if (Tok.is(tok::code_completion)) {
     cutOffParsing();
     Actions.CodeCompletion().CodeCompleteOrdinaryName(
@@ -1969,26 +2074,30 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
     ConsumeToken();
   } else if (getLangOpts().CPlusPlus && Tok.is(tok::identifier) &&
              isForRangeIdentifier()) {
+    // Note: This path is solely for error recovery if a user omits the type-id
+    // and writes 'for (x : ...)'; normally, the for-range-declaration is parsed
+    // in the 'if (isForInitDeclaration())' branch below.
     ProhibitAttributes(attrs);
     IdentifierInfo *Name = Tok.getIdentifierInfo();
     SourceLocation Loc = ConsumeToken();
     MaybeParseCXX11Attributes(attrs);
 
     ForRangeInfo.ColonLoc = ConsumeToken();
-    if (Tok.is(tok::l_brace))
-      ForRangeInfo.RangeExpr = ParseBraceInitializer();
-    else
-      ForRangeInfo.RangeExpr = ParseExpression();
+    ParseForRangeInitializerAfterColon(ForRangeInfo, /*VarDeclSpec=*/nullptr);
 
     Diag(Loc, diag::err_for_range_identifier)
-      << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17)
-              ? FixItHint::CreateInsertion(Loc, "auto &&")
-              : FixItHint());
-
-    ForRangeInfo.LoopVar =
-        Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs);
+        << (ForRangeInfo.ExpansionStmt != nullptr)
+        << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17)
+                ? FixItHint::CreateInsertion(Loc, "auto &&")
+                : FixItHint());
+
+    if (!ForRangeInfo.ExpansionStmt)
+      ForRangeInfo.LoopVar =
+          Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs);
   } else if (isForInitDeclaration()) {  // for (int X = 4;
     ParenBraceBracketBalancer BalancerRAIIObj(*this);
+    ExpansionStmtContextRAII EnterParentContext{
+        Actions, ForRangeInfo, Actions.CurContext->getParent()};
 
     // Parse declaration, which eats the ';'.
     if (!C99orCXXorObjC) {   // Use of C99-style for loops in C90 mode?
@@ -2041,6 +2150,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
       }
     }
   } else {
+    // An expression here should not be inside the expansion statement context.
+    ExpansionStmtContextRAII EnterParentContext{
+      Actions, ForRangeInfo, Actions.CurContext->getParent()};
     ProhibitAttributes(attrs);
     Value = ParseExpression();
 
@@ -2078,7 +2190,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
       // User tried to write the reasonable, but ill-formed, for-range-statement
       //   for (expr : expr) { ... }
       Diag(Tok, diag::err_for_range_expected_decl)
-        << FirstPart.get()->getSourceRange();
+          << (ESD != nullptr)
+          << FirstPart.get()->getSourceRange();
       SkipUntil(tok::r_paren, StopBeforeMatch);
       SecondPart = Sema::ConditionError();
     } else {
@@ -2200,7 +2313,13 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
   StmtResult ForRangeStmt;
   StmtResult ForEachStmt;
 
-  if (ForRangeInfo.ParsedForRangeDecl()) {
+  if (ESD) {
+    ForRangeStmt = Actions.ActOnCXXExpansionStmtPattern(
+        ESD, FirstPart.get(), ForRangeInfo.LoopVar.get(),
+        ForRangeInfo.RangeExpr.get(), T.getOpenLocation(),
+        ForRangeInfo.ColonLoc, T.getCloseLocation(),
+        ForRangeInfo.LifetimeExtendTemps);
+  } else if (ForRangeInfo.ParsedForRangeDecl()) {
     ForRangeStmt = Actions.ActOnCXXForRangeStmt(
         getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(),
         ForRangeInfo.LoopVar.get(), ForRangeInfo.ColonLoc,
@@ -2222,7 +2341,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
   // OpenACC Restricts a for-loop inside of certain construct/clause
   // combinations, so diagnose that here in OpenACC mode.
   SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()};
-  if (ForRangeInfo.ParsedForRangeDecl())
+  if (ESD)
+    ; // Nothing.
+  else if (ForRangeInfo.ParsedForRangeDecl())
     getActions().OpenACC().ActOnRangeForStmtBegin(ForLoc, ForRangeStmt.get());
   else
     getActions().OpenACC().ActOnForStmtBegin(
@@ -2276,6 +2397,15 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
     return Actions.ObjC().FinishObjCForCollectionStmt(ForEachStmt.get(),
                                                       Body.get());
 
+  if (ESD) {
+    if (!ForRangeInfo.ParsedForRangeDecl()) {
+      Diag(ForLoc, diag::err_expansion_stmt_requires_range);
+      return StmtError();
+    }
+
+    return Actions.FinishCXXExpansionStmt(ForRangeStmt.get(), Body.get());
+  }
+
   if (ForRangeInfo.ParsedForRangeDecl())
     return Actions.FinishCXXForRangeStmt(ForRangeStmt.get(), Body.get());
 
@@ -2657,6 +2787,37 @@ StmtResult Parser::ParseCXXCatchBlock(bool FnCatch) {
   return Actions.ActOnCXXCatchBlock(CatchLoc, ExceptionDecl, Block.get());
 }
 
+StmtResult Parser::ParseExpansionStatement(SourceLocation *TrailingElseLoc,
+                                           LabelDecl *PrecedingLabel,
+                                           SourceLocation TemplateLoc) {
+  assert(Tok.is(tok::kw_for));
+
+  CXXExpansionStmtDecl *ExpansionDecl =
+      Actions.ActOnCXXExpansionStmtDecl(TemplateParameterDepth, TemplateLoc);
+
+  CXXExpansionStmtPattern *Expansion;
+  {
+    Sema::ContextRAII CtxGuard(Actions, ExpansionDecl, /*NewThis=*/false);
+    TemplateParameterDepthRAII TParamDepthGuard(TemplateParameterDepth);
+    ++TParamDepthGuard;
+
+    StmtResult SR =
+        ParseForStatement(TrailingElseLoc, PrecedingLabel, ExpansionDecl);
+    if (SR.isInvalid())
+      return SR;
+
+    Expansion = cast<CXXExpansionStmtPattern>(SR.get());
+    ExpansionDecl->setExpansionPattern(Expansion);
+  }
+
+  DeclSpec DS(AttrFactory);
+  DeclGroupPtrTy DeclGroupPtr =
+      Actions.FinalizeDeclaratorGroup(getCurScope(), DS, {ExpansionDecl});
+
+  return Actions.ActOnDeclStmt(DeclGroupPtr, Expansion->getBeginLoc(),
+                               Expansion->getEndLoc());
+}
+
 void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) {
   IfExistsCondition Result;
   if (ParseMicrosoftIfExistsCondition(Result))
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 0ebf56ecffe69..ef729e22c1dc8 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -53,6 +53,7 @@ add_clang_library(clangSema
   SemaDeclCXX.cpp
   SemaDeclObjC.cpp
   SemaExceptionSpec.cpp
+  SemaExpand.cpp
   SemaExpr.cpp
   SemaExprCXX.cpp
   SemaExprMember.cpp
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 16a34475aac1a..3e22afe0da59d 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -1384,7 +1384,8 @@ void Sema::PushDeclContext(Scope *S, DeclContext *DC) {
   assert(DC->getLexicalParent() == CurContext &&
       "The next DeclContext should be lexically contained in the current one.");
   CurContext = DC;
-  S->setEntity(DC);
+  if (S)
+    S->setEntity(DC);
 }
 
 void Sema::PopDeclContext() {
@@ -14778,14 +14779,15 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
   }
 }
 
-void Sema::ActOnCXXForRangeDecl(Decl *D) {
+void Sema::ActOnCXXForRangeDecl(Decl *D, bool InExpansionStmt) {
   // If there is no declaration, there was an error parsing it. Ignore it.
   if (!D)
     return;
 
   VarDecl *VD = dyn_cast<VarDecl>(D);
   if (!VD) {
-    Diag(D->getLocation(), diag::err_for_range_decl_must_be_var);
+    Diag(D->getLocation(), diag::err_for_range_decl_must_be_var)
+        << InExpansionStmt;
     D->setInvalidDecl();
     return;
   }
@@ -14827,7 +14829,7 @@ void Sema::ActOnCXXForRangeDecl(Decl *D) {
 
   if (Error != -1) {
     Diag(VD->getOuterLocStart(), diag::err_for_range_storage_class)
-        << VD << Error;
+        << InExpansionStmt << VD << Error;
     D->setInvalidDecl();
   }
 }
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index b3a02a3fd5d02..deb6342e94ef4 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -2027,6 +2027,9 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
       //   - using-enum-declaration
       continue;
 
+    case Decl::CXXExpansionStmt:
+      continue;
+
     case Decl::Typedef:
     case Decl::TypeAlias: {
       //   - typedef declarations and alias-declarations that do not define
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
new file mode 100644
index 0000000000000..8647276149d9a
--- /dev/null
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -0,0 +1,641 @@
+//===-- SemaExpand.cpp - Semantic Analysis for Expansion Statements--------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements semantic analysis for C++26 expansion statements,
+//  aka 'template for'.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Overload.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/Template.h"
+#include "llvm/ADT/ScopeExit.h"
+
+using namespace clang;
+
+namespace {
+struct IterableExpansionStmtData {
+  /// When we try to determine whether an expansion statement is iterating, the
+  /// result can be
+  ///
+  ///   1. it is not iterating (it could still be destructuring; we don't
+  ///      know that yet);
+  ///
+  ///   2. there was a hard error (e.g. we determined that it is iterating,
+  ///      but begin() is deleted);
+  ///
+  ///   3. it is iterating, and there was no error.
+  enum class IsIterableResult {
+    NotIterable, ///< Not iterable, but also not invalid.
+    Error,       ///< Ill-formed.
+    Iterable,    ///< Iterable (and there was no error).
+  };
+
+  DeclStmt *RangeDecl = nullptr;
+  DeclStmt *BeginDecl = nullptr;
+  DeclStmt *IterDecl = nullptr;
+  IsIterableResult TheState = IsIterableResult::NotIterable;
+
+  bool isIterable() const { return TheState == IsIterableResult::Iterable; }
+  bool hasError() { return TheState == IsIterableResult::Error; }
+};
+} // namespace
+
+// Build a 'DeclRefExpr' designating the template parameter that is used as
+// the expansion index
+static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) {
+  return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(),
+                            S.Context.getPointerDiffType(), VK_PRValue,
+                            ESD->getBeginLoc());
+}
+
+static bool FinalizeExpansionVar(Sema &S, VarDecl *ExpansionVar,
+                                 ExprResult Initializer) {
+  if (Initializer.isInvalid()) {
+    S.ActOnInitializerError(ExpansionVar);
+    return true;
+  }
+
+  S.AddInitializerToDecl(ExpansionVar, Initializer.get(), /*DirectInit=*/false);
+  return ExpansionVar->isInvalidDecl();
+}
+
+static auto InitListContainsPack(const InitListExpr *ILE) {
+  return llvm::any_of(ILE->inits(),
+                      [](const Expr *E) { return isa<PackExpansionExpr>(E); });
+}
+
+static bool HasDependentSize(const DeclContext *CurContext,
+                             const CXXExpansionStmtPattern *Pattern) {
+  switch (Pattern->getKind()) {
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: {
+    auto *SelectExpr = cast<CXXExpansionSelectExpr>(
+        Pattern->getExpansionVariable()->getInit());
+    return InitListContainsPack(SelectExpr->getRangeExpr());
+  }
+
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
+    // Even if the size isn't technically dependent, delay expansion until
+    // we're no longer in a template since evaluating a lambda declared in
+    // a template doesn't work too well.
+    assert(CurContext->isExpansionStmt());
+    return CurContext->getParent()->isDependentContext();
+
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent:
+    return true;
+
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
+    return false;
+  }
+
+  llvm_unreachable("invalid pattern kind");
+}
+
+static IterableExpansionStmtData TryBuildIterableExpansionStmtInitializer(
+    Sema &S, Expr *ExpansionInitializer, Expr *Index, SourceLocation ColonLoc,
+    bool VarIsConstexpr,
+    ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+  IterableExpansionStmtData Data;
+
+  // C++26 [stmt.expand]p3: An expression is expansion-iterable if it does not
+  // have array type [...]
+  QualType Ty = ExpansionInitializer->getType().getNonReferenceType();
+  if (Ty->isArrayType())
+    return Data;
+
+  // Lookup member and ADL 'begin()'/'end()'. Only check if they exist; even if
+  // they're deleted, inaccessible, etc., this is still an iterating expansion
+  // statement, albeit an ill-formed one.
+  DeclarationNameInfo BeginName(&S.PP.getIdentifierTable().get("begin"),
+                                ColonLoc);
+  DeclarationNameInfo EndName(&S.PP.getIdentifierTable().get("end"), ColonLoc);
+
+  bool FoundBeginEnd = false;
+  if (auto *Record = Ty->getAsCXXRecordDecl()) {
+    LookupResult BeginLR(S, BeginName, Sema::LookupMemberName);
+    LookupResult EndLR(S, EndName, Sema::LookupMemberName);
+    FoundBeginEnd = S.LookupQualifiedName(BeginLR, Record) &&
+                    S.LookupQualifiedName(EndLR, Record);
+  }
+
+  // If member lookup doesn't yield anything, try ADL.
+  //
+  // If overload resolution for 'begin()' *and* 'end()' succeeds (irrespective
+  // of whether it results in a usable candidate), then assume this is an
+  // iterating expansion statement.
+  auto HasADLCandidate = [&](DeclarationName Name) {
+    OverloadCandidateSet Candidates(ColonLoc, OverloadCandidateSet::CSK_Normal);
+    OverloadCandidateSet::iterator Best;
+
+    S.AddArgumentDependentLookupCandidates(Name, ColonLoc, ExpansionInitializer,
+                                           /*ExplicitTemplateArgs=*/nullptr,
+                                           Candidates);
+
+    return Candidates.BestViableFunction(S, ColonLoc, Best) !=
+           OR_No_Viable_Function;
+  };
+
+  if (!FoundBeginEnd && (!HasADLCandidate(BeginName.getName()) ||
+                         !HasADLCandidate(EndName.getName())))
+    return Data;
+
+  auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
+  if (VarIsConstexpr)
+    Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+  EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx);
+
+  // The declarations should be attached to the parent decl context.
+  Sema::ContextRAII CtxGuard(S, S.CurContext->getParent(),
+                             /*NewThis=*/false);
+
+  // We know that this is supposed to be an iterable expansion statement;
+  // delegate to the for-range code to build the range/begin/end variables.
+  //
+  // Any failure at this point is a hard error.
+  Data.TheState = IterableExpansionStmtData::IsIterableResult::Error;
+  Scope *Scope = S.getCurScope();
+
+  // By [stmt.expand]p5.2 (CWG3131), the declaration of 'range' is of the form
+  //
+  //     constexpr[opt] decltype(auto) range = (expansion-initializer);
+  //
+  // where 'constexpr' is present iff the for-range-declaration is 'constexpr'.
+  StmtResult Var = S.BuildCXXForRangeRangeVar(
+      Scope, S.ActOnParenExpr(ColonLoc, ColonLoc, ExpansionInitializer).get(),
+      S.Context.getAutoType(DeducedKind::Undeduced, QualType(),
+                            AutoTypeKeyword::DecltypeAuto),
+      VarIsConstexpr);
+  if (Var.isInvalid())
+    return Data;
+
+  // [stmt.expand]p5.2 (CWG3140): The keyword 'constexpr' is present in the
+  // declarations of 'range', 'begin', and 'iter' if and only if 'constexpr' is
+  // one of the decl-specifiers of the decl-specifier-seq of the
+  // for-range-declaration.
+  //
+  // FIXME: As of CWG3140, we should only create 'begin' here, and not 'end',
+  // but that requires another substantial refactor of the for-range code.
+  auto *RangeVar = cast<DeclStmt>(Var.get());
+  Sema::ForRangeBeginEndInfo Info = S.BuildCXXForRangeBeginEndVars(
+      Scope, cast<VarDecl>(RangeVar->getSingleDecl()), ColonLoc,
+      /*CoawaitLoc=*/{},
+      /*LifetimeExtendTemps=*/{}, Sema::BFRK_Build, VarIsConstexpr);
+
+  if (!Info.isValid())
+    return Data;
+
+  // At runtime, we only need to evaluate 'begin', whereas 'end' is only used at
+  // compile-time; we'll rebuild the latter when we compute the expansion size,
+  // so only build a DeclStmt for 'begin' here.
+  StmtResult BeginStmt = S.ActOnDeclStmt(
+      S.ConvertDeclToDeclGroup(Info.BeginVar), ColonLoc, ColonLoc);
+  if (BeginStmt.isInvalid())
+    return Data;
+
+  // TODO: Build 'constexpr auto iter = begin + decltype(begin - begin){i};'.
+  S.Diag(ColonLoc, diag::err_iterating_expansion_stmt_unsupported);
+  return Data;
+
+#if 0 // This will be used once we support iterating expansion statements.
+  // Store it in a variable.
+  // See also Sema::BuildCXXForRangeBeginEndVars().
+  const auto DepthStr = std::to_string(Scope->getDepth() / 2);
+  IdentifierInfo *Name =
+      S.PP.getIdentifierInfo(std::string("__iter") + DepthStr);
+  VarDecl *IterVar = S.BuildForRangeVarDecl(
+      ColonLoc, S.Context.getAutoDeductType(), Name, VarIsConstexpr);
+  S.AddInitializerToDecl(IterVar, BeginPlusI.get(), /*DirectInit=*/false);
+  if (IterVar->isInvalidDecl())
+    return Data;
+
+  StmtResult IterVarStmt =
+      S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(IterVar), ColonLoc, ColonLoc);
+  if (IterVarStmt.isInvalid())
+    return Data;
+
+  // CWG 3149: Apply lifetime extension to iterating expansion statements.
+  S.ApplyForRangeOrExpansionStatementLifetimeExtension(
+      cast<VarDecl>(RangeVar->getSingleDecl()), LifetimeExtendTemps);
+
+  Data.BeginDecl = BeginStmt.getAs<DeclStmt>();
+  Data.RangeDecl = RangeVar;
+  Data.IterDecl = IterVarStmt.getAs<DeclStmt>();
+  Data.TheState = IterableExpansionStmtData::IsIterableResult::Iterable;
+  return Data;
+#endif
+}
+
+static StmtResult BuildDestructuringDecompositionDecl(
+    Sema &S, Expr *ExpansionInitializer, SourceLocation ColonLoc,
+    bool VarIsConstexpr,
+    ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+  auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
+  if (VarIsConstexpr)
+    Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+  EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx);
+
+  // The declarations should be attached to the parent decl context.
+  Sema::ContextRAII CtxGuard(S, S.CurContext->getParent(),
+                             /*NewThis=*/false);
+
+  UnsignedOrNone Arity =
+      S.GetDecompositionElementCount(ExpansionInitializer->getType(), ColonLoc);
+
+  if (!Arity)
+    return StmtError();
+
+  QualType AutoRRef = S.Context.getAutoRRefDeductType();
+  SmallVector<BindingDecl *> Bindings;
+  for (unsigned I = 0; I < *Arity; ++I)
+    Bindings.push_back(BindingDecl::Create(
+        S.Context, S.CurContext, ColonLoc,
+        S.getPreprocessor().getIdentifierInfo("__u" + std::to_string(I)),
+        AutoRRef));
+
+  TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(AutoRRef);
+  auto *DD =
+      DecompositionDecl::Create(S.Context, S.CurContext, ColonLoc, ColonLoc,
+                                AutoRRef, TSI, SC_Auto, Bindings);
+
+  if (VarIsConstexpr)
+    DD->setConstexpr(true);
+
+  S.ApplyForRangeOrExpansionStatementLifetimeExtension(DD, LifetimeExtendTemps);
+  S.AddInitializerToDecl(DD, ExpansionInitializer, false);
+  return S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(DD), ColonLoc, ColonLoc);
+}
+
+CXXExpansionStmtDecl *
+Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
+                                SourceLocation TemplateKWLoc) {
+  // Create a template parameter. This will be used to denote the index
+  // of the element that we're instantiating. This type must be 'ptrdiff_t'
+  // for iterating expansion statements ([stmt.expand]p5.2), so use that in
+  // all cases.
+  QualType ParmTy = Context.getPointerDiffType();
+  TypeSourceInfo *ParmTI =
+      Context.getTrivialTypeSourceInfo(ParmTy, TemplateKWLoc);
+
+  auto *TParam = NonTypeTemplateParmDecl::Create(
+      Context, Context.getTranslationUnitDecl(), TemplateKWLoc, TemplateKWLoc,
+      TemplateDepth, /*Position=*/0, /*Id=*/nullptr, ParmTy, /*ParameterPack=*/false,
+      ParmTI);
+
+  return BuildCXXExpansionStmtDecl(CurContext, TemplateKWLoc, TParam);
+}
+
+CXXExpansionStmtDecl *
+Sema::BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc,
+                                NonTypeTemplateParmDecl *NTTP) {
+  auto *Result =
+      CXXExpansionStmtDecl::Create(Context, Ctx, TemplateKWLoc, NTTP);
+  Ctx->addDecl(Result);
+  return Result;
+}
+
+ExprResult Sema::ActOnCXXExpansionInitList(MultiExprArg SubExprs,
+                                           SourceLocation LBraceLoc,
+                                           SourceLocation RBraceLoc) {
+  return new (Context) InitListExpr(Context, LBraceLoc, SubExprs, RBraceLoc);
+}
+
+StmtResult Sema::ActOnCXXExpansionStmtPattern(
+    CXXExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt,
+    Expr *ExpansionInitializer, SourceLocation LParenLoc,
+    SourceLocation ColonLoc, SourceLocation RParenLoc,
+    ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+  if (!ExpansionInitializer || ExpansionInitializer->containsErrors() ||
+      !ExpansionVarStmt)
+    return StmtError();
+
+  assert(CurContext->isExpansionStmt());
+  auto *DS = cast<DeclStmt>(ExpansionVarStmt);
+  if (!DS->isSingleDecl()) {
+    Diag(DS->getBeginLoc(), diag::err_type_defined_in_for_range);
+    return StmtError();
+  }
+
+  VarDecl *ExpansionVar = dyn_cast<VarDecl>(DS->getSingleDecl());
+  if (!ExpansionVar || ExpansionVar->isInvalidDecl())
+    return StmtError();
+
+  // This is an enumerating expansion statement.
+  if (auto *ILE = dyn_cast<InitListExpr>(ExpansionInitializer)) {
+    assert(ILE->isSyntacticForm());
+    ExprResult Initializer =
+        BuildCXXExpansionSelectExpr(ILE, BuildIndexDRE(*this, ESD));
+    if (FinalizeExpansionVar(*this, ExpansionVar, Initializer))
+      return StmtError();
+
+    // TODO: CWG3043 (lifetime extension in enumerating expansion statements).
+    return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc,
+                                                   ColonLoc, RParenLoc);
+  }
+
+  if (ExpansionInitializer->hasPlaceholderType()) {
+    ExprResult R = CheckPlaceholderExpr(ExpansionInitializer);
+    if (R.isInvalid())
+      return StmtError();
+    ExpansionInitializer = R.get();
+  }
+
+  if (DiagnoseUnexpandedParameterPack(ExpansionInitializer))
+    return StmtError();
+
+  return BuildNonEnumeratingCXXExpansionStmtPattern(
+      ESD, Init, DS, ExpansionInitializer, LParenLoc, ColonLoc, RParenLoc,
+      LifetimeExtendTemps);
+}
+
+StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern(
+    Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc,
+    SourceLocation ColonLoc, SourceLocation RParenLoc) {
+  return CXXExpansionStmtPattern::CreateEnumerating(
+      Context, cast<CXXExpansionStmtDecl>(ESD), Init,
+      cast<DeclStmt>(ExpansionVar), LParenLoc, ColonLoc, RParenLoc);
+}
+
+StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
+    CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt,
+    Expr *ExpansionInitializer, SourceLocation LParenLoc,
+    SourceLocation ColonLoc, SourceLocation RParenLoc,
+    ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+  VarDecl *ExpansionVar = cast<VarDecl>(ExpansionVarStmt->getSingleDecl());
+
+  // Reject lambdas early.
+  if (auto *RD = ExpansionInitializer->getType()->getAsCXXRecordDecl();
+      RD && RD->isLambda()) {
+    Diag(ExpansionInitializer->getBeginLoc(), diag::err_expansion_stmt_lambda);
+    return StmtError();
+  }
+
+  if (ExpansionInitializer->isTypeDependent()) {
+    ActOnDependentForRangeInitializer(ExpansionVar, BFRK_Build);
+    return CXXExpansionStmtPattern::CreateDependent(
+        Context, ESD, Init, ExpansionVarStmt, ExpansionInitializer, LParenLoc,
+        ColonLoc, RParenLoc);
+  }
+
+  if (RequireCompleteType(ExpansionInitializer->getExprLoc(),
+                          ExpansionInitializer->getType(),
+                          diag::err_expansion_stmt_incomplete))
+    return StmtError();
+
+  if (ExpansionInitializer->getType()->isVariableArrayType()) {
+    Diag(ExpansionInitializer->getExprLoc(), diag::err_expansion_stmt_vla)
+        << ExpansionInitializer->getType();
+    return StmtError();
+  }
+
+  // Otherwise, if it can be an iterating expansion statement, it is one.
+  DeclRefExpr *Index = BuildIndexDRE(*this, ESD);
+  IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer(
+      *this, ExpansionInitializer, Index, ColonLoc, ExpansionVar->isConstexpr(),
+      LifetimeExtendTemps);
+  if (Data.hasError()) {
+    ActOnInitializerError(ExpansionVar);
+    return StmtError();
+  }
+
+  if (Data.isIterable()) {
+    // Build '*iter'.
+    auto *IterVar = cast<VarDecl>(Data.IterDecl->getSingleDecl());
+    DeclRefExpr *IterDRE = BuildDeclRefExpr(
+        IterVar, IterVar->getType().getNonReferenceType(), VK_LValue, ColonLoc);
+    ExprResult Deref =
+        ActOnUnaryOp(getCurScope(), ColonLoc, tok::star, IterDRE);
+    if (Deref.isInvalid()) {
+      ActOnInitializerError(ExpansionVar);
+      return StmtError();
+    }
+
+    Deref = MaybeCreateExprWithCleanups(Deref.get());
+
+    if (FinalizeExpansionVar(*this, ExpansionVar, Deref.get()))
+      return StmtError();
+
+    return CXXExpansionStmtPattern::CreateIterating(
+        Context, ESD, Init, ExpansionVarStmt, Data.RangeDecl, Data.BeginDecl,
+        Data.IterDecl, LParenLoc, ColonLoc, RParenLoc);
+  }
+
+  // If not, try destructuring.
+  StmtResult DecompDeclStmt = BuildDestructuringDecompositionDecl(
+      *this, ExpansionInitializer, ColonLoc, ExpansionVar->isConstexpr(),
+      LifetimeExtendTemps);
+  if (DecompDeclStmt.isInvalid()) {
+    Diag(ExpansionInitializer->getBeginLoc(),
+         diag::err_expansion_stmt_invalid_init)
+        << ExpansionInitializer->getType()
+        << ExpansionInitializer->getSourceRange();
+
+    ActOnInitializerError(ExpansionVar);
+    return StmtError();
+  }
+
+  auto *DS = DecompDeclStmt.getAs<DeclStmt>();
+  auto *DD = cast<DecompositionDecl>(DS->getSingleDecl());
+  if (DD->isInvalidDecl())
+    return StmtError();
+
+  // Synthesise an InitListExpr to store the bindings; this essentially lets us
+  // desugar the expansion of a destructuring expansion statement to that of an
+  // enumerating expansion statement.
+  SmallVector<Expr *> Bindings;
+  Bindings.reserve(DD->bindings().size());
+  for (BindingDecl *BD : DD->bindings()) {
+    Expr *Element = BuildDeclRefExpr(BD, BD->getType().getNonReferenceType(),
+                                     VK_LValue, ColonLoc);
+
+    // [stmt.expand]p5.3 (CWG3149): If the expansion-initializer is an lvalue,
+    // then vi is ui; otherwise, vi is static_cast<decltype(ui)&&>(ui).
+    if (!ExpansionInitializer->isLValue()) {
+      QualType Ty =
+          BuildReferenceType(getDecltypeForExpr(Element), /*LValueRef=*/false,
+                             ColonLoc, /*Entity=*/DeclarationName());
+      TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(Ty);
+      ExprResult Cast = BuildCXXNamedCast(
+          ColonLoc, tok::kw_static_cast, TSI, Element,
+          SourceRange(ColonLoc, ColonLoc), SourceRange(ColonLoc, ColonLoc));
+      assert(!Cast.isInvalid() && "cast to rvalue reference type failed?");
+      Element = Cast.get();
+    }
+
+    Bindings.push_back(Element);
+  }
+
+  ExprResult Select = BuildCXXExpansionSelectExpr(
+      new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc),
+      Index);
+
+  if (Select.isInvalid()) {
+    ActOnInitializerError(ExpansionVar);
+    return StmtError();
+  }
+
+  if (FinalizeExpansionVar(*this, ExpansionVar, Select))
+    return StmtError();
+
+  return CXXExpansionStmtPattern::CreateDestructuring(
+      Context, ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc);
+}
+
+StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
+  if (!Exp || !Body)
+    return StmtError();
+
+  auto *Expansion = cast<CXXExpansionStmtPattern>(Exp);
+  assert(!Expansion->getDecl()->getInstantiations() &&
+         "should not rebuild expansion statement after instantiation");
+
+  Expansion->setBody(Body);
+  if (HasDependentSize(CurContext, Expansion))
+    return Expansion;
+
+  // Now that we're expanding this, exit the context of the expansion stmt
+  // so that we no longer treat this as dependent.
+  ContextRAII CtxGuard(*this, CurContext->getParent(),
+                       /*NewThis=*/false);
+
+  // This can fail if this is an iterating expansion statement.
+  std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion);
+  if (!NumInstantiations)
+    return StmtError();
+
+  // Collect preamble statements.
+  //
+  // There are at most 3 of these: for iterating expansion statements, these
+  // consist of the '__range' and '__begin' variables, and for destructuring
+  // expansion statements of the DecompositionDecl whose initializer we're
+  // expanding. Finally, any expansion statement may have an init-statement
+  // as well.
+  SmallVector<Stmt *, 3> Preamble;
+  if (Expansion->getInit())
+    Preamble.push_back(Expansion->getInit());
+
+  if (Expansion->isIterating()) {
+    Preamble.push_back(Expansion->getRangeVarStmt());
+    Preamble.push_back(Expansion->getBeginVarStmt());
+  } else if (Expansion->isDestructuring()) {
+    Preamble.push_back(Expansion->getDecompositionDeclStmt());
+    MarkAnyDeclReferenced(Exp->getBeginLoc(), Expansion->getDecompositionDecl(),
+                          true);
+  }
+
+  // Return an empty statement if the range is empty.
+  if (*NumInstantiations == 0) {
+    Expansion->getDecl()->setInstantiations(
+        CXXExpansionStmtInstantiation::Create(Context, Expansion->getDecl(),
+                                              /*Instantiations=*/{}, Preamble,
+                                              Expansion->isDestructuring()));
+    return Expansion;
+  }
+
+  // Create a compound statement binding the expansion variable and body,
+  // as well as the 'iter' variable if this is an iterating expansion statement.
+  SmallVector<Stmt *, 3> StmtsToInstantiate;
+  if (Expansion->isIterating())
+    StmtsToInstantiate.push_back(Expansion->getIterVarStmt());
+  StmtsToInstantiate.push_back(Expansion->getExpansionVarStmt());
+  StmtsToInstantiate.push_back(Body);
+  Stmt *CombinedBody =
+      CompoundStmt::Create(Context, StmtsToInstantiate, FPOptionsOverride(),
+                           Body->getBeginLoc(), Body->getEndLoc());
+
+  // Expand the body for each instantiation.
+  SmallVector<Stmt *, 4> Instantiations;
+  CXXExpansionStmtDecl *ESD = Expansion->getDecl();
+  for (uint64_t I = 0; I < *NumInstantiations; ++I) {
+    TemplateArgument Arg{Context, llvm::APSInt::get(I),
+                         Context.getPointerDiffType()};
+    MultiLevelTemplateArgumentList MTArgList(ESD, Arg, true);
+    MTArgList.addOuterRetainedLevels(
+        Expansion->getDecl()->getIndexTemplateParm()->getDepth());
+
+    LocalInstantiationScope LIScope(*this, /*CombineWithOuterScope=*/true);
+    NonSFINAEContext _(*this);
+    InstantiatingTemplate Inst(*this, Body->getBeginLoc(), Expansion, Arg,
+                               Body->getSourceRange());
+
+    StmtResult Instantiation = SubstStmt(CombinedBody, MTArgList);
+    if (Instantiation.isInvalid())
+      return StmtError();
+    Instantiations.push_back(Instantiation.get());
+  }
+
+  auto *InstantiationsStmt = CXXExpansionStmtInstantiation::Create(
+      Context, Expansion->getDecl(), Instantiations, Preamble,
+      Expansion->isDestructuring() || Expansion->isIterating());
+
+  Expansion->getDecl()->setInstantiations(InstantiationsStmt);
+  return Expansion;
+}
+
+ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) {
+  if (Idx->isValueDependent() || InitListContainsPack(Range))
+    return new (Context) CXXExpansionSelectExpr(Context, Range, Idx);
+
+  // The index is a DRE to a template parameter; we should never
+  // fail to evaluate it.
+  uint64_t I = Idx->EvaluateKnownConstInt(Context).getZExtValue();
+  return Range->getInit(I);
+}
+
+std::optional<uint64_t>
+Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
+  if (Expansion->isEnumerating())
+    return cast<CXXExpansionSelectExpr>(
+               Expansion->getExpansionVariable()->getInit())
+        ->getRangeExpr()
+        ->getNumInits();
+
+  // [stmt.expand]p5.2 (CWG3131): N is the result of evaluating the expression
+  //
+  // [&] consteval {
+  //    std::ptrdiff_t result = 0;
+  //    auto b = begin-expr;
+  //    auto e = end-expr;
+  //    for (; b != e; ++b) ++result;
+  //    return result;
+  // }()
+  if (Expansion->isIterating()) {
+    SourceLocation Loc = Expansion->getColonLoc();
+    EnterExpressionEvaluationContext ExprEvalCtx(
+        *this, ExpressionEvaluationContext::ConstantEvaluated);
+
+    // TODO: Build the lambda and evaluate it.
+    Diag(Loc, diag::err_iterating_expansion_stmt_unsupported);
+    return std::nullopt;
+
+#if 0 // This will be used once we support iterating expansion statements.
+    Expr::EvalResult ER;
+    SmallVector<PartialDiagnosticAt, 4> Notes;
+    ER.Diag = &Notes;
+    if (!Call.get()->EvaluateAsInt(ER, Context)) {
+      Diag(Loc, diag::err_expansion_size_expr_not_ice);
+      for (const auto &[Location, PDiag] : Notes)
+        Diag(Location, PDiag);
+      return std::nullopt;
+    }
+
+    // It shouldn't be possible for this to be negative since we compute this
+    // via the built-in '++' on a ptrdiff_t.
+    assert(ER.Val.getInt().isNonNegative());
+    return ER.Val.getInt().getZExtValue();
+#endif
+  }
+
+  assert(Expansion->isDestructuring());
+  return Expansion->getDecompositionDecl()->bindings().size();
+}
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 9502b440dbe97..ed07395c00e72 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -4468,7 +4468,8 @@ LabelDecl *Sema::LookupExistingLabel(IdentifierInfo *II, SourceLocation Loc) {
 }
 
 LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc,
-                                     SourceLocation GnuLabelLoc) {
+                                     SourceLocation GnuLabelLoc,
+                                     bool IsLabelStmt) {
   if (GnuLabelLoc.isValid()) {
     // Local label definitions always shadow existing labels.
     auto *Res = LabelDecl::Create(Context, CurContext, Loc, II, GnuLabelLoc);
@@ -4477,15 +4478,43 @@ LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc,
     return cast<LabelDecl>(Res);
   }
 
-  // Not a GNU local label.
-  LabelDecl *Res = LookupExistingLabel(II, Loc);
-  if (!Res) {
-    // If not forward referenced or defined already, create the backing decl.
-    Res = LabelDecl::Create(Context, CurContext, Loc, II);
-    Scope *S = CurScope->getFnParent();
-    assert(S && "Not in a function?");
-    PushOnScopeChains(Res, S, true);
+  LabelDecl *Existing = LookupExistingLabel(II, Loc);
+
+  // C++26 [stmt.label]p4 An identifier label shall not be enclosed by an
+  // expansion-statement.
+  //
+  // As an extension, we allow GNU local labels since they are logically
+  // scoped to the containing block, which prevents us from ending up with
+  // multiple copies of the same label in a function after instantiation.
+  //
+  // While allowing this is slightly more complicated, it also has the nice
+  // side-effect of avoiding otherwise rather horrible diagnostics you'd get
+  // when trying to use '__label__' if we didn't support this.
+  if (IsLabelStmt && CurContext->isExpansionStmt()) {
+    if (Existing && Existing->isGnuLocal())
+      return Existing;
+
+    // Drop the label from the AST as creating it anyway would cause us to
+    // either issue various unhelpful diagnostics (if we were to declare
+    // it in the function decl context) or shadow a valid label with the
+    // same name outside the expansion statement.
+    Diag(Loc, diag::err_expansion_stmt_label);
+    return nullptr;
   }
+
+  if (Existing)
+    return Existing;
+
+  // Declare non-local labels outside any expansion statements; this is required
+  // to support jumping out of an expansion statement.
+  ContextRAII Ctx{*this, CurContext->getEnclosingNonExpansionStatementContext(),
+                  /*NewThisContext=*/false};
+
+  // Not a GNU local label. Create the backing decl.
+  auto *Res = LabelDecl::Create(Context, CurContext, Loc, II);
+  Scope *S = CurScope->getFnParent();
+  assert(S && "Not in a function?");
+  PushOnScopeChains(Res, S, true);
   return Res;
 }
 
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index b74af55d1bea1..406a6da0903fb 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -528,6 +528,25 @@ Sema::ActOnCaseExpr(SourceLocation CaseLoc, ExprResult Val) {
   return CheckAndFinish(Val.get());
 }
 
+static bool DiagnoseSwitchCaseInExpansionStmt(Sema &S, SourceLocation KwLoc,
+                                              bool IsDefault) {
+  // C++26 [stmt.expand] The compound-statement of an expansion-statement is a
+  // control-flow-limited statement.
+  //
+  // We diagnose this here rather than in JumpDiagnostics because those run
+  // after the expansion statement is instantiated, at which point we will have
+  // have already complained about duplicate case labels, which is not exactly
+  // great QOI.
+  if (S.CurContext->isExpansionStmt() &&
+      S.getCurFunction()->SwitchStack.back().EnclosingDC != S.CurContext) {
+    S.Diag(KwLoc, diag::err_expansion_stmt_case) << IsDefault;
+    S.Diag(S.getCurFunction()->SwitchStack.back().getPointer()->getSwitchLoc(),
+           diag::note_enclosing_switch_statement_here);
+    return true;
+  }
+  return false;
+}
+
 StmtResult
 Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal,
                     SourceLocation DotDotDotLoc, ExprResult RHSVal,
@@ -547,6 +566,9 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal,
     return StmtError();
   }
 
+  if (DiagnoseSwitchCaseInExpansionStmt(*this, CaseLoc, false))
+    return StmtError();
+
   if (LangOpts.OpenACC &&
       getCurScope()->isInOpenACCComputeConstructScope(Scope::SwitchScope)) {
     Diag(CaseLoc, diag::err_acc_branch_in_out_compute_construct)
@@ -572,6 +594,9 @@ Sema::ActOnDefaultStmt(SourceLocation DefaultLoc, SourceLocation ColonLoc,
     return SubStmt;
   }
 
+  if (DiagnoseSwitchCaseInExpansionStmt(*this, DefaultLoc, true))
+    return StmtError();
+
   if (LangOpts.OpenACC &&
       getCurScope()->isInOpenACCComputeConstructScope(Scope::SwitchScope)) {
     Diag(DefaultLoc, diag::err_acc_branch_in_out_compute_construct)
@@ -1196,8 +1221,9 @@ StmtResult Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc,
 
   auto *SS = SwitchStmt::Create(Context, InitStmt, Cond.get().first, CondExpr,
                                 LParenLoc, RParenLoc);
+  SS->setSwitchLoc(SwitchLoc);
   getCurFunction()->SwitchStack.push_back(
-      FunctionScopeInfo::SwitchInfo(SS, false));
+      FunctionScopeInfo::SwitchInfo(SS, CurContext));
   return SS;
 }
 
@@ -1313,7 +1339,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
     BodyStmt = new (Context) NullStmt(BodyStmt->getBeginLoc());
   }
 
-  SS->setBody(BodyStmt, SwitchLoc);
+  SS->setBody(BodyStmt);
 
   Expr *CondExpr = SS->getCond();
   if (!CondExpr) return StmtError();
@@ -2431,27 +2457,54 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E,
   SemaRef.Diag(Loc, diag::note_for_range_begin_end)
     << BEF << IsTemplate << Description << E->getType();
 }
+} // namespace
 
 /// Build a variable declaration for a for-range statement.
-VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc,
-                              QualType Type, StringRef Name) {
-  DeclContext *DC = SemaRef.CurContext;
-  IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name);
-  TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc);
-  VarDecl *Decl = VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type,
-                                  TInfo, SC_None);
+VarDecl *Sema::BuildForRangeVarDecl(SourceLocation Loc, QualType Type,
+                                    IdentifierInfo *II, bool IsConstexpr) {
+  // Making the variable constexpr doesn't automatically add 'const' to the
+  // type, so do that now.
+  if (IsConstexpr && !Type->isReferenceType())
+    Type = Type.withConst();
+
+  DeclContext *DC = CurContext;
+  TypeSourceInfo *TInfo = Context.getTrivialTypeSourceInfo(Type, Loc);
+  VarDecl *Decl =
+      VarDecl::Create(Context, DC, Loc, Loc, II, Type, TInfo, SC_None);
   Decl->setImplicit();
   Decl->setCXXForRangeImplicitVar(true);
+  if (IsConstexpr)
+    // CWG3044 changed this from 'static constexpr' to 'constexpr'.
+    Decl->setConstexpr(true);
   return Decl;
 }
 
-}
-
 static bool ObjCEnumerationCollection(Expr *Collection) {
   return !Collection->isTypeDependent()
           && Collection->getType()->getAs<ObjCObjectPointerType>() != nullptr;
 }
 
+StmtResult Sema::BuildCXXForRangeRangeVar(Scope *S, Expr *Range, QualType Type,
+                                          bool IsConstexpr) {
+
+  // Divide by 2, since the variables are in the inner scope (loop body).
+  const auto DepthStr = std::to_string(S->getDepth() / 2);
+  IdentifierInfo *Name =
+      PP.getIdentifierInfo(std::string("__range") + DepthStr);
+  SourceLocation RangeLoc = Range->getBeginLoc();
+  VarDecl *RangeVar = BuildForRangeVarDecl(
+      RangeLoc, Type, Name, IsConstexpr);
+  if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
+                            diag::err_for_range_deduction_failure))
+
+    return StmtError();
+
+  // Claim the type doesn't contain auto: we've already done the checking.
+  DeclGroupPtrTy RangeGroup =
+      BuildDeclaratorGroup(MutableArrayRef<Decl *>((Decl **)&RangeVar, 1));
+  return ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc);
+}
+
 StmtResult Sema::ActOnCXXForRangeStmt(
     Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
     Stmt *First, SourceLocation ColonLoc, Expr *Range, SourceLocation RParenLoc,
@@ -2496,22 +2549,8 @@ StmtResult Sema::ActOnCXXForRangeStmt(
   }
 
   // Build  auto && __range = range-init
-  // Divide by 2, since the variables are in the inner scope (loop body).
-  const auto DepthStr = std::to_string(S->getDepth() / 2);
-  SourceLocation RangeLoc = Range->getBeginLoc();
-  VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc,
-                                           Context.getAutoRRefDeductType(),
-                                           std::string("__range") + DepthStr);
-  if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
-                            diag::err_for_range_deduction_failure)) {
-    ActOnInitializerError(LoopVar);
-    return StmtError();
-  }
-
-  // Claim the type doesn't contain auto: we've already done the checking.
-  DeclGroupPtrTy RangeGroup =
-      BuildDeclaratorGroup(MutableArrayRef<Decl *>((Decl **)&RangeVar, 1));
-  StmtResult RangeDecl = ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc);
+  auto RangeDecl =
+      BuildCXXForRangeRangeVar(S, Range, Context.getAutoRRefDeductType());
   if (RangeDecl.isInvalid()) {
     ActOnInitializerError(LoopVar);
     return StmtError();
@@ -2710,6 +2749,224 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
       AdjustedRange.get(), RParenLoc, Sema::BFRK_Rebuild);
 }
 
+void Sema::ApplyForRangeOrExpansionStatementLifetimeExtension(
+    VarDecl *RangeVar, ArrayRef<MaterializeTemporaryExpr *> Temporaries) {
+  if (Temporaries.empty())
+    return;
+
+  InitializedEntity Entity = InitializedEntity::InitializeVariable(RangeVar);
+  for (auto *MTE : Temporaries)
+    MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber());
+}
+
+Sema::ForRangeBeginEndInfo Sema::BuildCXXForRangeBeginEndVars(
+    Scope *S, VarDecl *RangeVar, SourceLocation ColonLoc,
+    SourceLocation CoawaitLoc,
+    ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps,
+    BuildForRangeKind Kind, bool IsConstexpr, StmtResult *RebuildResult,
+    llvm::function_ref<StmtResult()> RebuildWithDereference,
+    IdentifierInfo *BeginName, IdentifierInfo *EndName) {
+  QualType RangeVarType = RangeVar->getType();
+  SourceLocation RangeLoc = RangeVar->getLocation();
+  const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType();
+
+  ExprResult BeginRangeRef =
+      BuildDeclRefExpr(RangeVar, RangeVarNonRefType, VK_LValue, ColonLoc);
+  if (BeginRangeRef.isInvalid())
+    return {};
+
+  ExprResult EndRangeRef =
+      BuildDeclRefExpr(RangeVar, RangeVarNonRefType, VK_LValue, ColonLoc);
+  if (EndRangeRef.isInvalid())
+    return {};
+
+  QualType AutoType = Context.getAutoDeductType();
+  Expr *Range = RangeVar->getInit();
+  if (!Range)
+    return {};
+  QualType RangeType = Range->getType();
+
+  if (RequireCompleteType(RangeLoc, RangeType,
+                          diag::err_for_range_incomplete_type))
+    return {};
+
+  // Build auto __begin = begin-expr, __end = end-expr.
+  // Divide by 2, since the variables are in the inner scope (loop body).
+  const auto DepthStr = std::to_string(S->getDepth() / 2);
+  if (!BeginName)
+    BeginName = PP.getIdentifierInfo(std::string("__begin") + DepthStr);
+  if (!EndName)
+    EndName = PP.getIdentifierInfo(std::string("__end") + DepthStr);
+  VarDecl *BeginVar = BuildForRangeVarDecl(
+      ColonLoc, AutoType, BeginName, IsConstexpr);
+  VarDecl *EndVar = BuildForRangeVarDecl(
+      ColonLoc, AutoType, EndName, IsConstexpr);
+
+  // Build begin-expr and end-expr and attach to __begin and __end variables.
+  ExprResult BeginExpr, EndExpr;
+  if (const ArrayType *UnqAT = RangeType->getAsArrayTypeUnsafe()) {
+    // - if _RangeT is an array type, begin-expr and end-expr are __range and
+    //   __range + __bound, respectively, where __bound is the array bound. If
+    //   _RangeT is an array of unknown size or an array of incomplete type,
+    //   the program is ill-formed;
+
+    // begin-expr is __range.
+    BeginExpr = BeginRangeRef;
+    if (!CoawaitLoc.isInvalid()) {
+      BeginExpr = ActOnCoawaitExpr(S, ColonLoc, BeginExpr.get());
+      if (BeginExpr.isInvalid())
+        return {};
+    }
+    if (FinishForRangeVarDecl(*this, BeginVar, BeginRangeRef.get(), ColonLoc,
+                              diag::err_for_range_iter_deduction_failure)) {
+      NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+      return {};
+    }
+
+    // Find the array bound.
+    ExprResult BoundExpr;
+    if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(UnqAT))
+      BoundExpr = IntegerLiteral::Create(
+          Context, CAT->getSize(), Context.getPointerDiffType(), RangeLoc);
+    else if (const VariableArrayType *VAT =
+                 dyn_cast<VariableArrayType>(UnqAT)) {
+      // For a variably modified type we can't just use the expression within
+      // the array bounds, since we don't want that to be re-evaluated here.
+      // Rather, we need to determine what it was when the array was first
+      // created - so we resort to using sizeof(vla)/sizeof(element).
+      // For e.g.
+      //  void f(int b) {
+      //    int vla[b];
+      //    b = -1;   <-- This should not affect the num of iterations below
+      //    for (int &c : vla) { .. }
+      //  }
+
+      // FIXME: This results in codegen generating IR that recalculates the
+      // run-time number of elements (as opposed to just using the IR Value
+      // that corresponds to the run-time value of each bound that was
+      // generated when the array was created.) If this proves too embarrassing
+      // even for unoptimized IR, consider passing a magic-value/cookie to
+      // codegen that then knows to simply use that initial llvm::Value (that
+      // corresponds to the bound at time of array creation) within
+      // getelementptr.  But be prepared to pay the price of increasing a
+      // customized form of coupling between the two components - which  could
+      // be hard to maintain as the codebase evolves.
+
+      ExprResult SizeOfVLAExprR = ActOnUnaryExprOrTypeTraitExpr(
+          EndVar->getLocation(), UETT_SizeOf,
+          /*IsType=*/true,
+          CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo(
+                                               VAT->desugar(), RangeLoc))
+              .getAsOpaquePtr(),
+          EndVar->getSourceRange());
+      if (SizeOfVLAExprR.isInvalid())
+        return {};
+
+      ExprResult SizeOfEachElementExprR = ActOnUnaryExprOrTypeTraitExpr(
+          EndVar->getLocation(), UETT_SizeOf,
+          /*IsType=*/true,
+          CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo(
+                                               VAT->getElementType(), RangeLoc))
+              .getAsOpaquePtr(),
+          EndVar->getSourceRange());
+      if (SizeOfEachElementExprR.isInvalid())
+        return {};
+
+      BoundExpr =
+          ActOnBinOp(S, EndVar->getLocation(), tok::slash, SizeOfVLAExprR.get(),
+                     SizeOfEachElementExprR.get());
+      if (BoundExpr.isInvalid())
+        return {};
+
+    } else {
+      // Can't be a DependentSizedArrayType or an IncompleteArrayType since
+      // UnqAT is not incomplete and Range is not type-dependent.
+      llvm_unreachable("Unexpected array type in for-range");
+    }
+
+    // end-expr is __range + __bound.
+    EndExpr =
+        ActOnBinOp(S, ColonLoc, tok::plus, EndRangeRef.get(), BoundExpr.get());
+    if (EndExpr.isInvalid())
+      return {};
+    if (FinishForRangeVarDecl(*this, EndVar, EndExpr.get(), ColonLoc,
+                              diag::err_for_range_iter_deduction_failure)) {
+      NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
+      return {};
+    }
+  } else {
+    OverloadCandidateSet CandidateSet(RangeLoc,
+                                      OverloadCandidateSet::CSK_Normal);
+    BeginEndFunction BEFFailure;
+    ForRangeStatus RangeStatus =
+        BuildNonArrayForRange(*this, BeginRangeRef.get(), EndRangeRef.get(),
+                              RangeType, BeginVar, EndVar, ColonLoc, CoawaitLoc,
+                              &CandidateSet, &BeginExpr, &EndExpr, &BEFFailure);
+
+    if (Kind == BFRK_Build && RangeStatus == FRS_NoViableFunction &&
+        BEFFailure == BEF_begin) {
+      // If the range is being built from an array parameter, emit a
+      // a diagnostic that it is being treated as a pointer.
+      if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Range)) {
+        if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) {
+          QualType ArrayTy = PVD->getOriginalType();
+          QualType PointerTy = PVD->getType();
+          if (PointerTy->isPointerType() && ArrayTy->isArrayType()) {
+            Diag(Range->getBeginLoc(), diag::err_range_on_array_parameter)
+                << RangeLoc << PVD << ArrayTy << PointerTy;
+            Diag(PVD->getLocation(), diag::note_declared_at);
+            return {};
+          }
+        }
+      }
+
+      // If building the range failed, try dereferencing the range expression
+      // unless a diagnostic was issued or the end function is problematic.
+      if (RebuildWithDereference) {
+        assert(RebuildResult);
+        StmtResult SR = RebuildWithDereference();
+        if (SR.isInvalid() || SR.isUsable()) {
+          *RebuildResult = SR;
+          return {};
+        }
+      }
+    }
+
+    // Otherwise, emit diagnostics if we haven't already.
+    if (RangeStatus == FRS_NoViableFunction) {
+      Expr *Range = BEFFailure ? EndRangeRef.get() : BeginRangeRef.get();
+      CandidateSet.NoteCandidates(
+          PartialDiagnosticAt(Range->getBeginLoc(),
+                              PDiag(diag::err_for_range_invalid)
+                                  << RangeLoc << Range->getType()
+                                  << BEFFailure),
+          *this, OCD_AllCandidates, Range);
+    }
+    // Return an error if no fix was discovered.
+    if (RangeStatus != FRS_Success)
+      return {};
+  }
+
+  assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() &&
+         "invalid range expression in for loop");
+
+  return {BeginVar, EndVar, BeginExpr.get(), EndExpr.get()};
+}
+
+void Sema::ActOnDependentForRangeInitializer(VarDecl *LoopVar,
+                                             BuildForRangeKind BFRK) {
+  // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill
+  // them in properly when we instantiate the loop.
+  if (!LoopVar->isInvalidDecl() && BFRK != BFRK_Check) {
+    if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar))
+      for (auto *Binding : DD->bindings()) {
+        if (!Binding->isParameterPack())
+          Binding->setType(Context.DependentTy);
+      }
+    LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
+  }
+}
+
 StmtResult Sema::BuildCXXForRangeStmt(
     SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
     SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End,
@@ -2741,208 +2998,37 @@ StmtResult Sema::BuildCXXForRangeStmt(
   if (RangeVarType->isDependentType()) {
     // The range is implicitly used as a placeholder when it is dependent.
     RangeVar->markUsed(Context);
-
-    // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill
-    // them in properly when we instantiate the loop.
-    if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) {
-      if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar))
-        for (auto *Binding : DD->bindings()) {
-          if (!Binding->isParameterPack())
-            Binding->setType(Context.DependentTy);
-        }
-      LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
-    }
+    ActOnDependentForRangeInitializer(LoopVar, Kind);
   } else if (!BeginDeclStmt.get()) {
-    SourceLocation RangeLoc = RangeVar->getLocation();
-
-    const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType();
-
-    ExprResult BeginRangeRef = BuildDeclRefExpr(RangeVar, RangeVarNonRefType,
-                                                VK_LValue, ColonLoc);
-    if (BeginRangeRef.isInvalid())
-      return StmtError();
-
-    ExprResult EndRangeRef = BuildDeclRefExpr(RangeVar, RangeVarNonRefType,
-                                              VK_LValue, ColonLoc);
-    if (EndRangeRef.isInvalid())
-      return StmtError();
+    StmtResult RebuildResult;
+    auto RebuildWithDereference = [&] {
+      return RebuildForRangeWithDereference(
+          *this, S, ForLoc, CoawaitLoc, InitStmt, LoopVarDecl, ColonLoc,
+          RangeVar->getInit(), RangeVar->getLocation(), RParenLoc);
+    };
 
-    QualType AutoType = Context.getAutoDeductType();
-    Expr *Range = RangeVar->getInit();
-    if (!Range)
-      return StmtError();
-    QualType RangeType = Range->getType();
+    auto BeginRangeRefTy = RangeVar->getType().getNonReferenceType();
+    ForRangeBeginEndInfo ForRangeInfo = BuildCXXForRangeBeginEndVars(
+        S, RangeVar, ColonLoc, CoawaitLoc, LifetimeExtendTemps, Kind,
+        /*Constexpr=*/false, &RebuildResult, RebuildWithDereference);
 
-    if (RequireCompleteType(RangeLoc, RangeType,
-                            diag::err_for_range_incomplete_type))
+    if (!RebuildResult.isUnset())
+      return RebuildResult;
+    if (!ForRangeInfo.isValid())
       return StmtError();
 
-    // Build auto __begin = begin-expr, __end = end-expr.
-    // Divide by 2, since the variables are in the inner scope (loop body).
-    const auto DepthStr = std::to_string(S->getDepth() / 2);
-    VarDecl *BeginVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType,
-                                             std::string("__begin") + DepthStr);
-    VarDecl *EndVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType,
-                                           std::string("__end") + DepthStr);
-
-    // Build begin-expr and end-expr and attach to __begin and __end variables.
-    ExprResult BeginExpr, EndExpr;
-    if (const ArrayType *UnqAT = RangeType->getAsArrayTypeUnsafe()) {
-      // - if _RangeT is an array type, begin-expr and end-expr are __range and
-      //   __range + __bound, respectively, where __bound is the array bound. If
-      //   _RangeT is an array of unknown size or an array of incomplete type,
-      //   the program is ill-formed;
-
-      // begin-expr is __range.
-      BeginExpr = BeginRangeRef;
-      if (!CoawaitLoc.isInvalid()) {
-        BeginExpr = ActOnCoawaitExpr(S, ColonLoc, BeginExpr.get());
-        if (BeginExpr.isInvalid())
-          return StmtError();
-      }
-      if (FinishForRangeVarDecl(*this, BeginVar, BeginRangeRef.get(), ColonLoc,
-                                diag::err_for_range_iter_deduction_failure)) {
-        NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
-        return StmtError();
-      }
-
-      // Find the array bound.
-      ExprResult BoundExpr;
-      if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(UnqAT))
-        BoundExpr = IntegerLiteral::Create(
-            Context, CAT->getSize(), Context.getPointerDiffType(), RangeLoc);
-      else if (const VariableArrayType *VAT =
-               dyn_cast<VariableArrayType>(UnqAT)) {
-        // For a variably modified type we can't just use the expression within
-        // the array bounds, since we don't want that to be re-evaluated here.
-        // Rather, we need to determine what it was when the array was first
-        // created - so we resort to using sizeof(vla)/sizeof(element).
-        // For e.g.
-        //  void f(int b) {
-        //    int vla[b];
-        //    b = -1;   <-- This should not affect the num of iterations below
-        //    for (int &c : vla) { .. }
-        //  }
-
-        // FIXME: This results in codegen generating IR that recalculates the
-        // run-time number of elements (as opposed to just using the IR Value
-        // that corresponds to the run-time value of each bound that was
-        // generated when the array was created.) If this proves too embarrassing
-        // even for unoptimized IR, consider passing a magic-value/cookie to
-        // codegen that then knows to simply use that initial llvm::Value (that
-        // corresponds to the bound at time of array creation) within
-        // getelementptr.  But be prepared to pay the price of increasing a
-        // customized form of coupling between the two components - which  could
-        // be hard to maintain as the codebase evolves.
-
-        ExprResult SizeOfVLAExprR = ActOnUnaryExprOrTypeTraitExpr(
-            EndVar->getLocation(), UETT_SizeOf,
-            /*IsType=*/true,
-            CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo(
-                                                 VAT->desugar(), RangeLoc))
-                .getAsOpaquePtr(),
-            EndVar->getSourceRange());
-        if (SizeOfVLAExprR.isInvalid())
-          return StmtError();
-
-        ExprResult SizeOfEachElementExprR = ActOnUnaryExprOrTypeTraitExpr(
-            EndVar->getLocation(), UETT_SizeOf,
-            /*IsType=*/true,
-            CreateParsedType(VAT->desugar(),
-                             Context.getTrivialTypeSourceInfo(
-                                 VAT->getElementType(), RangeLoc))
-                .getAsOpaquePtr(),
-            EndVar->getSourceRange());
-        if (SizeOfEachElementExprR.isInvalid())
-          return StmtError();
-
-        BoundExpr =
-            ActOnBinOp(S, EndVar->getLocation(), tok::slash,
-                       SizeOfVLAExprR.get(), SizeOfEachElementExprR.get());
-        if (BoundExpr.isInvalid())
-          return StmtError();
-
-      } else {
-        // Can't be a DependentSizedArrayType or an IncompleteArrayType since
-        // UnqAT is not incomplete and Range is not type-dependent.
-        llvm_unreachable("Unexpected array type in for-range");
-      }
-
-      // end-expr is __range + __bound.
-      EndExpr = ActOnBinOp(S, ColonLoc, tok::plus, EndRangeRef.get(),
-                           BoundExpr.get());
-      if (EndExpr.isInvalid())
-        return StmtError();
-      if (FinishForRangeVarDecl(*this, EndVar, EndExpr.get(), ColonLoc,
-                                diag::err_for_range_iter_deduction_failure)) {
-        NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
-        return StmtError();
-      }
-    } else {
-      OverloadCandidateSet CandidateSet(RangeLoc,
-                                        OverloadCandidateSet::CSK_Normal);
-      BeginEndFunction BEFFailure;
-      ForRangeStatus RangeStatus = BuildNonArrayForRange(
-          *this, BeginRangeRef.get(), EndRangeRef.get(), RangeType, BeginVar,
-          EndVar, ColonLoc, CoawaitLoc, &CandidateSet, &BeginExpr, &EndExpr,
-          &BEFFailure);
-
-      if (Kind == BFRK_Build && RangeStatus == FRS_NoViableFunction &&
-          BEFFailure == BEF_begin) {
-        // If the range is being built from an array parameter, emit a
-        // a diagnostic that it is being treated as a pointer.
-        if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Range)) {
-          if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) {
-            QualType ArrayTy = PVD->getOriginalType();
-            QualType PointerTy = PVD->getType();
-            if (PointerTy->isPointerType() && ArrayTy->isArrayType()) {
-              Diag(Range->getBeginLoc(), diag::err_range_on_array_parameter)
-                  << RangeLoc << PVD << ArrayTy << PointerTy;
-              Diag(PVD->getLocation(), diag::note_declared_at);
-              return StmtError();
-            }
-          }
-        }
-
-        // If building the range failed, try dereferencing the range expression
-        // unless a diagnostic was issued or the end function is problematic.
-        StmtResult SR = RebuildForRangeWithDereference(*this, S, ForLoc,
-                                                       CoawaitLoc, InitStmt,
-                                                       LoopVarDecl, ColonLoc,
-                                                       Range, RangeLoc,
-                                                       RParenLoc);
-        if (SR.isInvalid() || SR.isUsable())
-          return SR;
-      }
-
-      // Otherwise, emit diagnostics if we haven't already.
-      if (RangeStatus == FRS_NoViableFunction) {
-        Expr *Range = BEFFailure ? EndRangeRef.get() : BeginRangeRef.get();
-        CandidateSet.NoteCandidates(
-            PartialDiagnosticAt(Range->getBeginLoc(),
-                                PDiag(diag::err_for_range_invalid)
-                                    << RangeLoc << Range->getType()
-                                    << BEFFailure),
-            *this, OCD_AllCandidates, Range);
-      }
-      // Return an error if no fix was discovered.
-      if (RangeStatus != FRS_Success)
-        return StmtError();
-    }
-
-    assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() &&
-           "invalid range expression in for loop");
-
     // C++11 [dcl.spec.auto]p7: BeginType and EndType must be the same.
     // C++1z removes this restriction.
+    auto [BeginVar, EndVar, BeginExpr, EndExpr] = ForRangeInfo;
+    SourceLocation RangeLoc = RangeVar->getLocation();
     QualType BeginType = BeginVar->getType(), EndType = EndVar->getType();
     if (!Context.hasSameType(BeginType, EndType)) {
       Diag(RangeLoc, getLangOpts().CPlusPlus17
                          ? diag::warn_for_range_begin_end_types_differ
                          : diag::ext_for_range_begin_end_types_differ)
           << BeginType << EndType;
-      NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
-      NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
+      NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
+      NoteForRangeBeginEndFunction(*this, EndExpr, BEF_end);
     }
 
     BeginDeclStmt =
@@ -2971,10 +3057,10 @@ StmtResult Sema::BuildCXXForRangeStmt(
           ActOnFinishFullExpr(NotEqExpr.get(), /*DiscardedValue*/ false);
     if (NotEqExpr.isInvalid()) {
       Diag(RangeLoc, diag::note_for_range_invalid_iterator)
-        << RangeLoc << 0 << BeginRangeRef.get()->getType();
-      NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+          << RangeLoc << 0 << BeginRangeRefTy;
+      NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
       if (!Context.hasSameType(BeginType, EndType))
-        NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
+        NoteForRangeBeginEndFunction(*this, EndExpr, BEF_end);
       return StmtError();
     }
 
@@ -2994,8 +3080,8 @@ StmtResult Sema::BuildCXXForRangeStmt(
       IncrExpr = ActOnFinishFullExpr(IncrExpr.get(), /*DiscardedValue*/ false);
     if (IncrExpr.isInvalid()) {
       Diag(RangeLoc, diag::note_for_range_invalid_iterator)
-        << RangeLoc << 2 << BeginRangeRef.get()->getType() ;
-      NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+          << RangeLoc << 2 << BeginRangeRefTy;
+      NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
       return StmtError();
     }
 
@@ -3008,8 +3094,8 @@ StmtResult Sema::BuildCXXForRangeStmt(
     ExprResult DerefExpr = ActOnUnaryOp(S, ColonLoc, tok::star, BeginRef.get());
     if (DerefExpr.isInvalid()) {
       Diag(RangeLoc, diag::note_for_range_invalid_iterator)
-        << RangeLoc << 1 << BeginRangeRef.get()->getType();
-      NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+          << RangeLoc << 1 << BeginRangeRefTy;
+      NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
       return StmtError();
     }
 
@@ -3019,7 +3105,7 @@ StmtResult Sema::BuildCXXForRangeStmt(
       AddInitializerToDecl(LoopVar, DerefExpr.get(), /*DirectInit=*/false);
       if (LoopVar->isInvalidDecl() ||
           (LoopVar->getInit() && LoopVar->getInit()->containsErrors()))
-        NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+        NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
     }
   }
 
@@ -3034,11 +3120,9 @@ StmtResult Sema::BuildCXXForRangeStmt(
     OpenMP().ActOnOpenMPLoopInitialization(ForLoc, BeginDeclStmt.get());
 
   // P2718R0 - Lifetime extension in range-based for loops.
-  if (getLangOpts().CPlusPlus23 && !LifetimeExtendTemps.empty()) {
-    InitializedEntity Entity = InitializedEntity::InitializeVariable(RangeVar);
-    for (auto *MTE : LifetimeExtendTemps)
-      MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber());
-  }
+  if (getLangOpts().CPlusPlus23)
+    ApplyForRangeOrExpansionStatementLifetimeExtension(RangeVar,
+                                                       LifetimeExtendTemps);
 
   return new (Context) CXXForRangeStmt(
       InitStmt, RangeDS, cast_or_null<DeclStmt>(BeginDeclStmt.get()),
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 34ed5dffa11b4..296f0406acbaf 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -572,6 +572,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
   case PriorTemplateArgumentSubstitution:
   case ConstraintsCheck:
   case NestedRequirementConstraintsCheck:
+  case ExpansionStmtInstantiation:
     return true;
 
   case RequirementInstantiation:
@@ -760,6 +761,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
           PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
           /*Template=*/nullptr, /*TemplateArgs=*/{}) {}
 
+Sema::InstantiatingTemplate::InstantiatingTemplate(
+    Sema &SemaRef, SourceLocation PointOfInstantiation,
+    CXXExpansionStmtPattern *ExpansionStmt, ArrayRef<TemplateArgument> TArgs,
+    SourceRange InstantiationRange)
+    : InstantiatingTemplate(
+          SemaRef, CodeSynthesisContext::ExpansionStmtInstantiation,
+          PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
+          /*Template=*/nullptr, /*TemplateArgs=*/TArgs) {}
+
 Sema::InstantiatingTemplate::InstantiatingTemplate(
     Sema &SemaRef, SourceLocation PointOfInstantiation,
     concepts::NestedRequirement *Req, ConstraintsCheck,
@@ -1308,6 +1318,9 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) {
                                                 Active->NumCallArgs)));
       break;
     }
+    case CodeSynthesisContext::ExpansionStmtInstantiation:
+      Diags.Report(Active->PointOfInstantiation,
+                   diag::note_expansion_stmt_instantiation_here);
     }
   }
 }
@@ -1918,6 +1931,12 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
       maybeInstantiateFunctionParameterToScope(PVD))
     return nullptr;
 
+  if (isa<CXXExpansionStmtDecl>(D)) {
+    assert(SemaRef.CurrentInstantiationScope);
+    return cast<Decl *>(
+        *SemaRef.CurrentInstantiationScope->findInstantiationOf(D));
+  }
+
   return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
 }
 
@@ -2322,9 +2341,13 @@ ExprResult
 TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E,
                                                        ValueDecl *PD) {
   typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
-  llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found
-    = getSema().CurrentInstantiationScope->findInstantiationOf(PD);
-  assert(Found && "no instantiation for parameter pack");
+  llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found =
+      getSema().CurrentInstantiationScope->getInstantiationOfIfExists(PD);
+
+  // This can happen when instantiating an expansion statement that contains
+  // a pack (e.g. `template for (auto x : {{ts...}})`).
+  if (!Found)
+    return E;
 
   Decl *TransformedDecl;
   if (DeclArgumentPack *Pack = dyn_cast<DeclArgumentPack *>(*Found)) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index d057476a012db..04d79991d4663 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2103,7 +2103,37 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
 
 Decl *TemplateDeclInstantiator::VisitCXXExpansionStmtDecl(
     CXXExpansionStmtDecl *OldESD) {
-  llvm_unreachable("TODO");
+  Decl *Index = VisitNonTypeTemplateParmDecl(OldESD->getIndexTemplateParm());
+  CXXExpansionStmtDecl *NewESD = SemaRef.BuildCXXExpansionStmtDecl(
+      Owner, OldESD->getBeginLoc(), cast<NonTypeTemplateParmDecl>(Index));
+  SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldESD, NewESD);
+
+  // If this was already expanded, only instantiate the expansion and
+  // don't touch the unexpanded expansion statement.
+  if (CXXExpansionStmtInstantiation *OldInst = OldESD->getInstantiations()) {
+    StmtResult NewInst = SemaRef.SubstStmt(OldInst, TemplateArgs);
+    if (NewInst.isInvalid())
+      return nullptr;
+
+    NewESD->setInstantiations(NewInst.getAs<CXXExpansionStmtInstantiation>());
+    NewESD->setExpansionPattern(OldESD->getExpansionPattern());
+    return NewESD;
+  }
+
+  // Enter the scope of this expansion statement; don't do this if we've
+  // already expanded it, as in that case we no longer want to treat its
+  // content as dependent.
+  Sema::ContextRAII Context(SemaRef, NewESD, /*NewThis=*/false);
+
+  StmtResult Expansion =
+      SemaRef.SubstStmt(OldESD->getExpansionPattern(), TemplateArgs);
+  if (Expansion.isInvalid())
+    return nullptr;
+
+  // The code that handles CXXExpansionStmtPattern takes care of calling
+  // setInstantiation() on the ESD if there was an expansion.
+  NewESD->setExpansionPattern(cast<CXXExpansionStmtPattern>(Expansion.get()));
+  return NewESD;
 }
 
 Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
@@ -7108,6 +7138,12 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
     // anonymous unions in class templates).
   }
 
+  if (CurrentInstantiationScope) {
+    if (auto Found = CurrentInstantiationScope->getInstantiationOfIfExists(D))
+      if (auto *FD = dyn_cast<NamedDecl>(cast<Decl *>(*Found)))
+        return FD;
+  }
+
   if (!ParentDependsOnArgs)
     return D;
 
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 5b1aad3fa8470..b61148bf95738 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -912,10 +912,14 @@ bool Sema::CheckParameterPacksForExpansion(
     unsigned NewPackSize, PendingPackExpansionSize = 0;
     if (IsVarDeclPack) {
       // Figure out whether we're instantiating to an argument pack or not.
+      //
+      // The instantiation may not exist; this can happen when instantiating an
+      // expansion statement that contains a pack (e.g.
+      // `template for (auto x : {{ts...}})`).
       llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
-          CurrentInstantiationScope->findInstantiationOf(
+          CurrentInstantiationScope->getInstantiationOfIfExists(
               cast<NamedDecl *>(ParmPack.first));
-      if (isa<DeclArgumentPack *>(*Instantiation)) {
+      if (Instantiation && isa<DeclArgumentPack *>(*Instantiation)) {
         // We could expand this function parameter pack.
         NewPackSize = cast<DeclArgumentPack *>(*Instantiation)->size();
       } else {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index ad8e0b76f1dcb..bf80bc1110ec5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9372,19 +9372,201 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
 template <typename Derived>
 StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
     CXXExpansionStmtPattern *S) {
-  llvm_unreachable("TOOD");
+  assert(SemaRef.CurContext->isExpansionStmt());
+
+  Decl *ESD =
+      getDerived().TransformDecl(S->getDecl()->getLocation(), S->getDecl());
+  if (!ESD || ESD->isInvalidDecl())
+    return StmtError();
+  CXXExpansionStmtDecl *NewESD = cast<CXXExpansionStmtDecl>(ESD);
+
+  // This is required because some parts of an expansion statement (e.g. the
+  // init-statement) are not in a dependent context and must thus be transformed
+  // in the parent context.
+  auto TransformStmtInParentContext = [&] (Stmt *SubStmt) -> StmtResult {
+    Sema::ContextRAII CtxGuard(SemaRef, SemaRef.CurContext->getParent(),
+                               /*NewThis=*/false);
+    return getDerived().TransformStmt(SubStmt);
+  };
+
+  Stmt *Init = S->getInit();
+  if (Init) {
+    StmtResult SR = TransformStmtInParentContext(Init);
+    if (SR.isInvalid())
+      return StmtError();
+    Init = SR.get();
+  }
+
+  // Collect lifetime-extended temporaries in case this ends up being a
+  // destructuring or iterating expansion statement.
+  //
+  // CWG 3140: Additionally, for iterating expansions statements, we need to
+  // apply lifetime extension to the initializer of the range.
+  ExprResult ExpansionInitializer;
+  StmtResult Range;
+  SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
+  if (S->isDependent() || S->isIterating()) {
+    EnterExpressionEvaluationContext ExprEvalCtx(
+        SemaRef, SemaRef.currentEvaluationContext().Context);
+    SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
+    SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true;
+
+    if (S->isDependent()) {
+      // The expansion initializer should not be in the context of the expansion
+      // statement because it isn't instantiated when the expansion statement is
+      // expanded.
+      Sema::ContextRAII CtxGuard(SemaRef, SemaRef.CurContext->getParent(),
+                                 /*NewThis=*/false);
+      ExpansionInitializer =
+          getDerived().TransformExpr(S->getExpansionInitializer());
+      if (ExpansionInitializer.isInvalid())
+        return StmtError();
+    } else if (S->isIterating()) {
+      Range = TransformStmtInParentContext(S->getRangeVarStmt());
+      if (Range.isInvalid())
+        return StmtError();
+    }
+
+    ExpansionInitializer =
+        SemaRef.MaybeCreateExprWithCleanups(ExpansionInitializer);
+
+    LifetimeExtendTemps =
+        SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps;
+  }
+
+  CXXExpansionStmtPattern *NewPattern = nullptr;
+  if (S->isEnumerating()) {
+    StmtResult ExpansionVar =
+        getDerived().TransformStmt(S->getExpansionVarStmt());
+    if (ExpansionVar.isInvalid())
+      return StmtError();
+
+    NewPattern = CXXExpansionStmtPattern::CreateEnumerating(
+        SemaRef.Context, NewESD, Init, ExpansionVar.getAs<DeclStmt>(),
+        S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc());
+  } else if (S->isIterating()) {
+    StmtResult Begin = TransformStmtInParentContext(S->getBeginVarStmt());
+    StmtResult Iter = TransformStmtInParentContext(S->getIterVarStmt());
+    if (Begin.isInvalid() || Iter.isInvalid())
+      return StmtError();
+
+    // The expansion variable is part of the pattern only and never ends
+    // up in the instantiations, so keep it in the expansion statement's
+    // DeclContext.
+    StmtResult ExpansionVar =
+        getDerived().TransformStmt(S->getExpansionVarStmt());
+    if (ExpansionVar.isInvalid())
+      return StmtError();
+
+    NewPattern = CXXExpansionStmtPattern::CreateIterating(
+        SemaRef.Context, NewESD, Init, ExpansionVar.getAs<DeclStmt>(),
+        Range.getAs<DeclStmt>(), Begin.getAs<DeclStmt>(),
+        Iter.getAs<DeclStmt>(), S->getLParenLoc(), S->getColonLoc(),
+        S->getRParenLoc());
+
+    SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension(
+        NewPattern->getRangeVar(), LifetimeExtendTemps);
+  } else if (S->isDependent()) {
+    StmtResult ExpansionVar =
+        getDerived().TransformStmt(S->getExpansionVarStmt());
+    if (ExpansionVar.isInvalid())
+      return StmtError();
+
+    StmtResult Res = SemaRef.BuildNonEnumeratingCXXExpansionStmtPattern(
+        NewESD, Init, ExpansionVar.getAs<DeclStmt>(),
+        ExpansionInitializer.get(), S->getLParenLoc(), S->getColonLoc(),
+        S->getRParenLoc(), LifetimeExtendTemps);
+
+    if (Res.isInvalid())
+      return StmtError();
+
+    NewPattern = cast<CXXExpansionStmtPattern>(Res.get());
+  } else {
+    // The only time we instantiate an expansion statement is if its expansion
+    // size is dependent (otherwise, we only instantiate the expansions and
+    // leave the underlying CXXExpansionStmtPattern as-is). Since destructuring
+    // expansion statements never have a dependent size, we should never get
+    // here.
+    llvm_unreachable("destructuring pattern should never be instantiated");
+  }
+
+  StmtResult Body = getDerived().TransformStmt(S->getBody());
+  if (Body.isInvalid())
+    return StmtError();
+
+  return SemaRef.FinishCXXExpansionStmt(NewPattern, Body.get());
 }
 
 template <typename Derived>
 StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
     CXXExpansionStmtInstantiation *S) {
-  llvm_unreachable("TOOD");
+  bool SubStmtChanged = false;
+  auto TransformStmts = [&](SmallVectorImpl<Stmt *> &NewStmts,
+                            ArrayRef<Stmt *> OldStmts) {
+    for (Stmt *OldDS : OldStmts) {
+      StmtResult NewDS = getDerived().TransformStmt(OldDS);
+      if (NewDS.isInvalid())
+        return true;
+
+      SubStmtChanged |= NewDS.get() != OldDS;
+      NewStmts.push_back(NewDS.get());
+    }
+
+    return false;
+  };
+
+  Decl *ESD =
+      getDerived().TransformDecl(S->getParent()->getLocation(), S->getParent());
+  if (!ESD || ESD->isInvalidDecl())
+    return StmtError();
+  CXXExpansionStmtDecl *NewESD = cast<CXXExpansionStmtDecl>(ESD);
+
+  SmallVector<Stmt *> PreambleStmts;
+  SmallVector<Stmt *> Instantiations;
+
+  // Apply lifetime extension to the preamble statements if this was a
+  // destructuring expansion statement.
+  {
+    EnterExpressionEvaluationContext ExprEvalCtx(
+        SemaRef, SemaRef.currentEvaluationContext().Context);
+    SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
+    SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true;
+    if (TransformStmts(PreambleStmts, S->getPreambleStmts()))
+      return StmtError();
+
+    if (S->shouldApplyLifetimeExtensionToPreamble()) {
+      auto *VD =
+          cast<VarDecl>(cast<DeclStmt>(PreambleStmts.front())->getSingleDecl());
+      SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension(
+          VD, SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps);
+    }
+  }
+
+  if (TransformStmts(Instantiations, S->getInstantiations()))
+    return StmtError();
+
+  if (!getDerived().AlwaysRebuild() && !SubStmtChanged)
+    return S;
+
+  return CXXExpansionStmtInstantiation::Create(
+      SemaRef.Context, NewESD, Instantiations, PreambleStmts,
+      S->shouldApplyLifetimeExtensionToPreamble());
 }
 
 template <typename Derived>
 ExprResult TreeTransform<Derived>::TransformCXXExpansionSelectExpr(
     CXXExpansionSelectExpr *E) {
-  llvm_unreachable("TOOD");
+  ExprResult Range = getDerived().TransformExpr(E->getRangeExpr());
+  ExprResult Idx = getDerived().TransformExpr(E->getIndexExpr());
+  if (Range.isInvalid() || Idx.isInvalid())
+    return ExprError();
+
+  if (!getDerived().AlwaysRebuild() && Range.get() == E->getRangeExpr() &&
+      Idx.get() == E->getIndexExpr())
+    return E;
+
+  return SemaRef.BuildCXXExpansionSelectExpr(Range.getAs<InitListExpr>(),
+                                             Idx.get());
 }
 
 template<typename Derived>
@@ -15826,7 +16008,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   // will be deemed as dependent even if there are no dependent template
   // arguments.
   // (A ClassTemplateSpecializationDecl is always a dependent context.)
-  while (DC->isRequiresExprBody())
+  while (DC->isRequiresExprBody() || isa<CXXExpansionStmtDecl>(DC))
     DC = DC->getParent();
   if ((getSema().isUnevaluatedContext() ||
        getSema().isConstantEvaluatedContext()) &&
diff --git a/clang/test/AST/ast-dump-expansion-stmt.cpp b/clang/test/AST/ast-dump-expansion-stmt.cpp
new file mode 100644
index 0000000000000..94f0ee1449f17
--- /dev/null
+++ b/clang/test/AST/ast-dump-expansion-stmt.cpp
@@ -0,0 +1,54 @@
+// Test without serialization:
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-unknown -ast-dump %s
+//
+// Test with serialization:
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-unknown -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -std=c++26 -triple x86_64-unknown-unknown -include-pch %t -ast-dump-all /dev/null \
+// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//"
+
+#if 0 // Disabled until we support iterating expansion statements.
+template <typename T, __SIZE_TYPE__ size>
+struct Array {
+  T data[size]{};
+  constexpr const T* begin() const { return data; }
+  constexpr const T* end() const { return data + size; }
+};
+#endif // 0
+
+void foo(int);
+
+template <typename T>
+void test(T t) {
+  // CHECK:      CXXExpansionStmtDecl
+  // CHECK-NEXT:   CXXExpansionStmtPattern {{.*}} enumerating
+  // CHECK:        CXXExpansionStmtInstantiation
+  template for (auto x : {1, 2, 3}) {
+    foo(x);
+  }
+
+#if 0 // Disabled until we support iterating expansion statements.
+  // NOTE: Remove 'DISABLED-' when the '#if 0' is removed.
+  // DISABLED-CHECK:      CXXExpansionStmtDecl
+  // DISABLED-CHECK-NEXT:   CXXExpansionStmtPattern {{.*}} iterating
+  // DISABLED-CHECK:        CXXExpansionStmtInstantiation
+  static constexpr Array<int, 3> a;
+  template for (auto x : a) {
+    foo(x);
+  }
+#endif
+
+  // CHECK:      CXXExpansionStmtDecl
+  // CHECK-NEXT:   CXXExpansionStmtPattern {{.*}} destructuring
+  // CHECK:        CXXExpansionStmtInstantiation
+  int arr[3]{1, 2, 3};
+  template for (auto x : arr) {
+    foo(x);
+  }
+
+  // CHECK:      CXXExpansionStmtDecl
+  // CHECK-NEXT:   CXXExpansionStmtPattern {{.*}} dependent
+  // CHECK-NOT:    CXXExpansionStmtInstantiation
+  template for (auto x : t) {
+    foo(x);
+  }
+}
diff --git a/clang/test/AST/ast-print-expansion-stmts.cpp b/clang/test/AST/ast-print-expansion-stmts.cpp
new file mode 100644
index 0000000000000..880e17a9d3e2f
--- /dev/null
+++ b/clang/test/AST/ast-print-expansion-stmts.cpp
@@ -0,0 +1,113 @@
+// Without serialization:
+// RUN: %clang_cc1 -std=c++26 -ast-print %s | FileCheck %s
+//
+// With serialization:
+// RUN: %clang_cc1 -std=c++26 -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -std=c++26 -include-pch %t -ast-print  /dev/null | FileCheck %s
+
+#if 0 // Disabled until we support iterating expansion statements.
+template <typename T, __SIZE_TYPE__ size>
+struct Array {
+  T data[size]{};
+  constexpr const T* begin() const { return data; }
+  constexpr const T* end() const { return data + size; }
+};
+#endif // 0
+
+// CHECK: void foo(int);
+void foo(int);
+
+// CHECK: template <typename T> void test(T t) {
+template <typename T>
+void test(T t) {
+  // Enumerating expansion statement.
+  //
+  // CHECK:      template for (auto x : {1, 2, 3}) {
+  // CHECK-NEXT:     foo(x);
+  // CHECK-NEXT: }
+  template for (auto x : {1, 2, 3}) {
+    foo(x);
+  }
+
+#if 0 // Disabled until we support iterating expansion statements.
+  // Iterating expansion statement.
+  //
+  // NOTE: Remove 'DISABLED-' when the '#if 0' is removed.
+  // DISABLED-CHECK:      static constexpr Array<int, 3> a;
+  // DISABLED-CHECK-NEXT: template for (auto x : (a)) {
+  // DISABLED-CHECK-NEXT:   foo(x);
+  // DISABLED-CHECK-NEXT: }
+  static constexpr Array<int, 3> a;
+  template for (auto x : a) {
+    foo(x);
+  }
+#endif // 0
+
+  // Destructuring expansion statement.
+  //
+  // CHECK:      int arr[3]{1, 2, 3};
+  // CHECK-NEXT: template for (auto x : arr) {
+  // CHECK-NEXT:   foo(x);
+  // CHECK-NEXT: }
+  int arr[3]{1, 2, 3};
+  template for (auto x : arr) {
+    foo(x);
+  }
+
+  // Dependent expansion statement.
+  //
+  // CHECK:      template for (auto x : t) {
+  // CHECK-NEXT:   foo(x);
+  // CHECK-NEXT: }
+  template for (auto x : t) {
+    foo(x);
+  }
+}
+
+// CHECK: template <typename T> void test2(T t) {
+template <typename T>
+void test2(T t) {
+  // Enumerating expansion statement.
+  //
+  // CHECK:      template for (int x : {1, 2, 3}) {
+  // CHECK-NEXT:     foo(x);
+  // CHECK-NEXT: }
+  template for (int x : {1, 2, 3}) {
+    foo(x);
+  }
+
+#if 0 // Disabled until we support iterating expansion statements.
+  // Iterating expansion statement.
+  //
+  // NOTE: Remove 'DISABLED-' when the '#if 0' is removed.
+  // DISABLED-CHECK:      static constexpr Array<int, 3> a;
+  // DISABLED-CHECK-NEXT: template for (int x : (a)) {
+  // DISABLED-CHECK-NEXT:   foo(x);
+  // DISABLED-CHECK-NEXT: }
+
+  static constexpr Array<int, 3> a;
+  template for (int x : a) {
+    foo(x);
+  }
+#endif // 0
+
+  // Destructuring expansion statement.
+  //
+  // CHECK:      int arr[3]{1, 2, 3};
+  // CHECK-NEXT: template for (int x : arr) {
+  // CHECK-NEXT:   foo(x);
+  // CHECK-NEXT: }
+  int arr[3]{1, 2, 3};
+  template for (int x : arr) {
+    foo(x);
+  }
+
+  // Dependent expansion statement.
+  //
+  // CHECK:      template for (int x : t) {
+  // CHECK-NEXT:   foo(x);
+  // CHECK-NEXT: }
+  template for (int x : t) {
+    foo(x);
+  }
+}
diff --git a/clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp b/clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp
new file mode 100644
index 0000000000000..64a3b20f81c6a
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp
@@ -0,0 +1,532 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+struct A {};
+struct B { int x = 1; };
+struct C { int a = 1, b = 2, c = 3; };
+
+void g(int);
+
+int references_destructuring() {
+  C c;
+  template for (auto& x : c) { ++x; }
+  template for (auto&& x : c) { ++x; }
+  return c.a + c.b + c.c;
+}
+
+template <auto v>
+int destructure() {
+  int sum = 0;
+  template for (auto x : v) sum += x;
+  template for (constexpr auto x : v) sum += x;
+  return sum;
+}
+
+void f() {
+  destructure<B{10}>();
+  destructure<C{}>();
+  destructure<C{3, 4, 5}>();
+}
+
+void empty() {
+  static constexpr A a;
+  template for (auto x : A()) g(x);
+  template for (auto& x : a) g(x);
+  template for (auto&& x : A()) g(x);
+  template for (constexpr auto x : a) g(x);
+}
+
+namespace apply_lifetime_extension {
+struct T {
+  int& x;
+  T(int& x) noexcept : x(x) {}
+  ~T() noexcept { x = 42; }
+};
+
+const T& f(const T& t) noexcept { return t; }
+T g(int& x) noexcept { return T(x); }
+
+// CWG 3043:
+//
+// Lifetime extension only applies to destructuring expansion statements
+// (enumerating statements don't have a range variable, and the range variable
+// of iterating statements is constexpr).
+int lifetime_extension() {
+  int x = 5;
+  int sum  = 0;
+  template for (auto e : f(g(x))) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+template <typename T>
+int lifetime_extension_instantiate_expansions() {
+  int x = 5;
+  int sum  = 0;
+  template for (T e : f(g(x))) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+template <typename T>
+int lifetime_extension_dependent_expansion_stmt() {
+  int x = 5;
+  int sum  = 0;
+  template for (int e : f(g((T&)x))) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+template <typename U>
+struct foo {
+  template <typename T>
+  int lifetime_extension_multiple_instantiations() {
+    int x = 5;
+    int sum  = 0;
+    template for (T e : f(g((U&)x))) {
+      sum += x;
+    }
+    return sum + x;
+  }
+};
+
+void instantiate() {
+  lifetime_extension_instantiate_expansions<int>();
+  lifetime_extension_dependent_expansion_stmt<int>();
+  foo<int>().lifetime_extension_multiple_instantiations<int>();
+}
+}
+
+struct Volatile {
+  volatile int x;
+};
+
+int volatile_member(Volatile* v) {
+  int sum = 0;
+  template for (auto& x : *v) {
+    sum += x;
+    x = 4;
+  }
+  return sum;
+}
+
+// CHECK: $_ZN1CC1Ev = comdat any
+// CHECK: $_Z11destructureITnDaXtl1BLi10EEEEiv = comdat any
+// CHECK: $_Z11destructureITnDaXtl1CLi1ELi2ELi3EEEEiv = comdat any
+// CHECK: $_Z11destructureITnDaXtl1CLi3ELi4ELi5EEEEiv = comdat any
+// CHECK: $_ZN24apply_lifetime_extension1TC1ERi = comdat any
+// CHECK: $_ZN24apply_lifetime_extension1TD1Ev = comdat any
+// CHECK: $_ZN24apply_lifetime_extension41lifetime_extension_instantiate_expansionsIiEEiv = comdat any
+// CHECK: $_ZN24apply_lifetime_extension43lifetime_extension_dependent_expansion_stmtIiEEiv = comdat any
+// CHECK: $_ZN24apply_lifetime_extension3fooIiE42lifetime_extension_multiple_instantiationsIiEEiv = comdat any
+// CHECK: $_ZN1CC2Ev = comdat any
+// CHECK: $_ZN24apply_lifetime_extension1TC2ERi = comdat any
+// CHECK: $_ZN24apply_lifetime_extension1TD2Ev = comdat any
+// CHECK: $_ZTAXtl1BLi10EEE = comdat any
+// CHECK: $_ZTAXtl1CLi1ELi2ELi3EEE = comdat any
+// CHECK: $_ZTAXtl1CLi3ELi4ELi5EEE = comdat any
+// CHECK: @_ZZ5emptyvE1a = internal constant %struct.A zeroinitializer, align 1
+// CHECK: @_ZTAXtl1BLi10EEE = {{.*}} constant %struct.B { i32 10 }, comdat
+// CHECK: @_ZTAXtl1CLi1ELi2ELi3EEE = {{.*}} constant %struct.C { i32 1, i32 2, i32 3 }, comdat
+// CHECK: @_ZTAXtl1CLi3ELi4ELi5EEE = {{.*}} constant %struct.C { i32 3, i32 4, i32 5 }, comdat
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z24references_destructuringv()
+// CHECK: entry:
+// CHECK-NEXT:   %c = alloca %struct.C, align 4
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca ptr, align 8
+// CHECK-NEXT:   %x1 = alloca ptr, align 8
+// CHECK-NEXT:   %x4 = alloca ptr, align 8
+// CHECK-NEXT:   %1 = alloca ptr, align 8
+// CHECK-NEXT:   %x7 = alloca ptr, align 8
+// CHECK-NEXT:   %x11 = alloca ptr, align 8
+// CHECK-NEXT:   %x15 = alloca ptr, align 8
+// CHECK-NEXT:   call void @_ZN1CC1Ev(ptr {{.*}} %c)
+// CHECK-NEXT:   store ptr %c, ptr %0, align 8
+// CHECK-NEXT:   %2 = load ptr, ptr %0, align 8
+// CHECK-NEXT:   %a = getelementptr inbounds nuw %struct.C, ptr %2, i32 0, i32 0
+// CHECK-NEXT:   store ptr %a, ptr %x, align 8
+// CHECK-NEXT:   %3 = load ptr, ptr %x, align 8
+// CHECK-NEXT:   %4 = load i32, ptr %3, align 4
+// CHECK-NEXT:   %inc = add nsw i32 %4, 1
+// CHECK-NEXT:   store i32 %inc, ptr %3, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %5 = load ptr, ptr %0, align 8
+// CHECK-NEXT:   %b = getelementptr inbounds nuw %struct.C, ptr %5, i32 0, i32 1
+// CHECK-NEXT:   store ptr %b, ptr %x1, align 8
+// CHECK-NEXT:   %6 = load ptr, ptr %x1, align 8
+// CHECK-NEXT:   %7 = load i32, ptr %6, align 4
+// CHECK-NEXT:   %inc2 = add nsw i32 %7, 1
+// CHECK-NEXT:   store i32 %inc2, ptr %6, align 4
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   %8 = load ptr, ptr %0, align 8
+// CHECK-NEXT:   %c5 = getelementptr inbounds nuw %struct.C, ptr %8, i32 0, i32 2
+// CHECK-NEXT:   store ptr %c5, ptr %x4, align 8
+// CHECK-NEXT:   %9 = load ptr, ptr %x4, align 8
+// CHECK-NEXT:   %10 = load i32, ptr %9, align 4
+// CHECK-NEXT:   %inc6 = add nsw i32 %10, 1
+// CHECK-NEXT:   store i32 %inc6, ptr %9, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store ptr %c, ptr %1, align 8
+// CHECK-NEXT:   %11 = load ptr, ptr %1, align 8
+// CHECK-NEXT:   %a8 = getelementptr inbounds nuw %struct.C, ptr %11, i32 0, i32 0
+// CHECK-NEXT:   store ptr %a8, ptr %x7, align 8
+// CHECK-NEXT:   %12 = load ptr, ptr %x7, align 8
+// CHECK-NEXT:   %13 = load i32, ptr %12, align 4
+// CHECK-NEXT:   %inc9 = add nsw i32 %13, 1
+// CHECK-NEXT:   store i32 %inc9, ptr %12, align 4
+// CHECK-NEXT:   br label %expand.next10
+// CHECK: expand.next10:
+// CHECK-NEXT:   %14 = load ptr, ptr %1, align 8
+// CHECK-NEXT:   %b12 = getelementptr inbounds nuw %struct.C, ptr %14, i32 0, i32 1
+// CHECK-NEXT:   store ptr %b12, ptr %x11, align 8
+// CHECK-NEXT:   %15 = load ptr, ptr %x11, align 8
+// CHECK-NEXT:   %16 = load i32, ptr %15, align 4
+// CHECK-NEXT:   %inc13 = add nsw i32 %16, 1
+// CHECK-NEXT:   store i32 %inc13, ptr %15, align 4
+// CHECK-NEXT:   br label %expand.next14
+// CHECK: expand.next14:
+// CHECK-NEXT:   %17 = load ptr, ptr %1, align 8
+// CHECK-NEXT:   %c16 = getelementptr inbounds nuw %struct.C, ptr %17, i32 0, i32 2
+// CHECK-NEXT:   store ptr %c16, ptr %x15, align 8
+// CHECK-NEXT:   %18 = load ptr, ptr %x15, align 8
+// CHECK-NEXT:   %19 = load i32, ptr %18, align 4
+// CHECK-NEXT:   %inc17 = add nsw i32 %19, 1
+// CHECK-NEXT:   store i32 %inc17, ptr %18, align 4
+// CHECK-NEXT:   br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT:   %a19 = getelementptr inbounds nuw %struct.C, ptr %c, i32 0, i32 0
+// CHECK-NEXT:   %20 = load i32, ptr %a19, align 4
+// CHECK-NEXT:   %b20 = getelementptr inbounds nuw %struct.C, ptr %c, i32 0, i32 1
+// CHECK-NEXT:   %21 = load i32, ptr %b20, align 4
+// CHECK-NEXT:   %add = add nsw i32 %20, %21
+// CHECK-NEXT:   %c21 = getelementptr inbounds nuw %struct.C, ptr %c, i32 0, i32 2
+// CHECK-NEXT:   %22 = load i32, ptr %c21, align 4
+// CHECK-NEXT:   %add22 = add nsw i32 %add, %22
+// CHECK-NEXT:   ret i32 %add22
+
+
+// CHECK-LABEL: define {{.*}} void @_Z1fv()
+// CHECK: entry:
+// CHECK-NEXT:   %call = call {{.*}} i32 @_Z11destructureITnDaXtl1BLi10EEEEiv()
+// CHECK-NEXT:   %call1 = call {{.*}} i32 @_Z11destructureITnDaXtl1CLi1ELi2ELi3EEEEiv()
+// CHECK-NEXT:   %call2 = call {{.*}} i32 @_Z11destructureITnDaXtl1CLi3ELi4ELi5EEEEiv()
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z11destructureITnDaXtl1BLi10EEEEiv()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %1 = alloca ptr, align 8
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store ptr @_ZTAXtl1BLi10EEE, ptr %0, align 8
+// CHECK-NEXT:   %2 = load i32, ptr @_ZTAXtl1BLi10EEE, align 4
+// CHECK-NEXT:   store i32 %2, ptr %x, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %4, %3
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store ptr @_ZTAXtl1BLi10EEE, ptr %1, align 8
+// CHECK-NEXT:   store i32 10, ptr %x1, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add2 = add nsw i32 %5, 10
+// CHECK-NEXT:   store i32 %add2, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end3
+// CHECK: expand.end3:
+// CHECK-NEXT:   %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %6
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z11destructureITnDaXtl1CLi1ELi2ELi3EEEEiv()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %x4 = alloca i32, align 4
+// CHECK-NEXT:   %1 = alloca ptr, align 8
+// CHECK-NEXT:   %x6 = alloca i32, align 4
+// CHECK-NEXT:   %x9 = alloca i32, align 4
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store ptr @_ZTAXtl1CLi1ELi2ELi3EEE, ptr %0, align 8
+// CHECK-NEXT:   %2 = load i32, ptr @_ZTAXtl1CLi1ELi2ELi3EEE, align 4
+// CHECK-NEXT:   store i32 %2, ptr %x, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %4, %3
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %5 = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZTAXtl1CLi1ELi2ELi3EEE, i64 4), align 4
+// CHECK-NEXT:   store i32 %5, ptr %x1, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x1, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add2 = add nsw i32 %7, %6
+// CHECK-NEXT:   store i32 %add2, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   %8 = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZTAXtl1CLi1ELi2ELi3EEE, i64 8), align 4
+// CHECK-NEXT:   store i32 %8, ptr %x4, align 4
+// CHECK-NEXT:   %9 = load i32, ptr %x4, align 4
+// CHECK-NEXT:   %10 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add5 = add nsw i32 %10, %9
+// CHECK-NEXT:   store i32 %add5, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store ptr @_ZTAXtl1CLi1ELi2ELi3EEE, ptr %1, align 8
+// CHECK-NEXT:   store i32 1, ptr %x6, align 4
+// CHECK-NEXT:   %11 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add7 = add nsw i32 %11, 1
+// CHECK-NEXT:   store i32 %add7, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT:   store i32 2, ptr %x9, align 4
+// CHECK-NEXT:   %12 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add10 = add nsw i32 %12, 2
+// CHECK-NEXT:   store i32 %add10, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT:   store i32 3, ptr %x12, align 4
+// CHECK-NEXT:   %13 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add13 = add nsw i32 %13, 3
+// CHECK-NEXT:   store i32 %add13, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT:   %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %14
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z11destructureITnDaXtl1CLi3ELi4ELi5EEEEiv()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %x4 = alloca i32, align 4
+// CHECK-NEXT:   %1 = alloca ptr, align 8
+// CHECK-NEXT:   %x6 = alloca i32, align 4
+// CHECK-NEXT:   %x9 = alloca i32, align 4
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store ptr @_ZTAXtl1CLi3ELi4ELi5EEE, ptr %0, align 8
+// CHECK-NEXT:   %2 = load i32, ptr @_ZTAXtl1CLi3ELi4ELi5EEE, align 4
+// CHECK-NEXT:   store i32 %2, ptr %x, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %4, %3
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %5 = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZTAXtl1CLi3ELi4ELi5EEE, i64 4), align 4
+// CHECK-NEXT:   store i32 %5, ptr %x1, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x1, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add2 = add nsw i32 %7, %6
+// CHECK-NEXT:   store i32 %add2, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   %8 = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZTAXtl1CLi3ELi4ELi5EEE, i64 8), align 4
+// CHECK-NEXT:   store i32 %8, ptr %x4, align 4
+// CHECK-NEXT:   %9 = load i32, ptr %x4, align 4
+// CHECK-NEXT:   %10 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add5 = add nsw i32 %10, %9
+// CHECK-NEXT:   store i32 %add5, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store ptr @_ZTAXtl1CLi3ELi4ELi5EEE, ptr %1, align 8
+// CHECK-NEXT:   store i32 3, ptr %x6, align 4
+// CHECK-NEXT:   %11 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add7 = add nsw i32 %11, 3
+// CHECK-NEXT:   store i32 %add7, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT:   store i32 4, ptr %x9, align 4
+// CHECK-NEXT:   %12 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add10 = add nsw i32 %12, 4
+// CHECK-NEXT:   store i32 %add10, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT:   store i32 5, ptr %x12, align 4
+// CHECK-NEXT:   %13 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add13 = add nsw i32 %13, 5
+// CHECK-NEXT:   store i32 %add13, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT:   %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %14
+
+
+// CHECK-LABEL: define {{.*}} void @_Z5emptyv()
+// CHECK: entry:
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK-NEXT:   %ref.tmp = alloca %struct.A, align 1
+// CHECK-NEXT:   %1 = alloca ptr, align 8
+// CHECK-NEXT:   %2 = alloca ptr, align 8
+// CHECK-NEXT:   %ref.tmp1 = alloca %struct.A, align 1
+// CHECK-NEXT:   %3 = alloca ptr, align 8
+// CHECK-NEXT:   store ptr %ref.tmp, ptr %0, align 8
+// CHECK-NEXT:   store ptr @_ZZ5emptyvE1a, ptr %1, align 8
+// CHECK-NEXT:   store ptr %ref.tmp1, ptr %2, align 8
+// CHECK-NEXT:   store ptr @_ZZ5emptyvE1a, ptr %3, align 8
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension18lifetime_extensionEv()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8
+// CHECK-NEXT:   %e = alloca i32, align 4
+// CHECK-NEXT:   store i32 5, ptr %x, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x)
+// CHECK-NEXT:   %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   store ptr %call, ptr %0, align 8
+// CHECK-NEXT:   %1 = load ptr, ptr %0, align 8
+// CHECK: %x1 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0
+// CHECK-NEXT:   %2 = load ptr, ptr %x1, align 8
+// CHECK-NEXT:   %3 = load i32, ptr %2, align 4
+// CHECK-NEXT:   store i32 %3, ptr %e, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %5, %4
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %add2 = add nsw i32 %6, %7
+// CHECK-NEXT:   ret i32 %add2
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension41lifetime_extension_instantiate_expansionsIiEEiv()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8
+// CHECK-NEXT:   %e = alloca i32, align 4
+// CHECK-NEXT:   store i32 5, ptr %x, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x)
+// CHECK-NEXT:   %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   store ptr %call, ptr %0, align 8
+// CHECK-NEXT:   %1 = load ptr, ptr %0, align 8
+// CHECK: %x1 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0
+// CHECK-NEXT:   %2 = load ptr, ptr %x1, align 8
+// CHECK-NEXT:   %3 = load i32, ptr %2, align 4
+// CHECK-NEXT:   store i32 %3, ptr %e, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %5, %4
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %add2 = add nsw i32 %6, %7
+// CHECK-NEXT:   ret i32 %add2
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension43lifetime_extension_dependent_expansion_stmtIiEEiv()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8
+// CHECK-NEXT:   %e = alloca i32, align 4
+// CHECK-NEXT:   store i32 5, ptr %x, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x)
+// CHECK-NEXT:   %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   store ptr %call, ptr %0, align 8
+// CHECK-NEXT:   %1 = load ptr, ptr %0, align 8
+// CHECK: %x1 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0
+// CHECK-NEXT:   %2 = load ptr, ptr %x1, align 8
+// CHECK-NEXT:   %3 = load i32, ptr %2, align 4
+// CHECK-NEXT:   store i32 %3, ptr %e, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %5, %4
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %add2 = add nsw i32 %6, %7
+// CHECK-NEXT:   ret i32 %add2
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension3fooIiE42lifetime_extension_multiple_instantiationsIiEEiv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8
+// CHECK-NEXT:   %e = alloca i32, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   store i32 5, ptr %x, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr {{.*}} %ref.tmp, ptr {{.*}} %x)
+// CHECK-NEXT:   %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   store ptr %call, ptr %0, align 8
+// CHECK-NEXT:   %1 = load ptr, ptr %0, align 8
+// CHECK: %x2 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0
+// CHECK-NEXT:   %2 = load ptr, ptr %x2, align 8
+// CHECK-NEXT:   %3 = load i32, ptr %2, align 4
+// CHECK-NEXT:   store i32 %3, ptr %e, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %5, %4
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %add3 = add nsw i32 %6, %7
+// CHECK-NEXT:   ret i32 %add3
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z15volatile_memberP8Volatile(ptr {{.*}} %v)
+// CHECK: entry:
+// CHECK-NEXT:   %v.addr = alloca ptr, align 8
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca ptr, align 8
+// CHECK-NEXT:   store ptr %v, ptr %v.addr, align 8
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   %1 = load ptr, ptr %v.addr, align 8
+// CHECK-NEXT:   store ptr %1, ptr %0, align 8
+// CHECK-NEXT:   %2 = load ptr, ptr %0, align 8
+// CHECK-NEXT:   %x1 = getelementptr inbounds nuw %struct.Volatile, ptr %2, i32 0, i32 0
+// CHECK-NEXT:   store ptr %x1, ptr %x, align 8
+// CHECK-NEXT:   %3 = load ptr, ptr %x, align 8
+// CHECK-NEXT:   %4 = load volatile i32, ptr %3, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %5, %4
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   %6 = load ptr, ptr %x, align 8
+// CHECK-NEXT:   store volatile i32 4, ptr %6, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %7
diff --git a/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp b/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp
new file mode 100644
index 0000000000000..c82b345de206b
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp
@@ -0,0 +1,1518 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+struct S {
+  int x;
+  constexpr S(int x) : x{x} {}
+};
+
+void g(int);
+void g(long);
+void g(const char*);
+void g(S);
+
+template <int n> constexpr int tg() { return n; }
+
+void h(int, int);
+
+void f1() {
+  template for (auto x : {1, 2, 3}) g(x);
+}
+
+void f2() {
+  template for (auto x : {1, "123", S(45)}) g(x);
+}
+
+void f3() {
+  template for (auto x : {}) g(x);
+}
+
+void f4() {
+  template for (auto x : {1, 2})
+    template for (auto y : {3, 4})
+      h(x, y);
+}
+
+void f5() {
+  template for (auto x : {}) static_assert(false, "discarded");
+  template for (constexpr auto x : {}) static_assert(false, "discarded");
+  template for (auto x : {1}) g(x);
+  template for (auto x : {2, 3, 4}) g(x);
+  template for (constexpr auto x : {5}) g(x);
+  template for (constexpr auto x : {6, 7, 8}) g(x);
+  template for (constexpr auto x : {9}) tg<x>();
+  template for (constexpr auto x : {10, 11, 12})
+    static_assert(tg<x>());
+
+  template for (int x : {13, 14, 15}) g(x);
+  template for (S x : {16, 17, 18}) g(x.x);
+  template for (constexpr S x : {19, 20, 21}) tg<x.x>();
+}
+
+template <typename T>
+void t1() {
+  template for (T x : {}) g(x);
+  template for (constexpr T x : {}) g(x);
+  template for (auto x : {}) g(x);
+  template for (constexpr auto x : {}) g(x);
+  template for (T x : {1, 2}) g(x);
+  template for (T x : {T(3), T(4)}) g(x);
+  template for (auto x : {T(5), T(6)}) g(x);
+  template for (constexpr T x : {T(7), T(8)}) static_assert(tg<x>());
+  template for (constexpr auto x : {T(9), T(10)}) static_assert(tg<x>());
+}
+
+template <typename U>
+struct s1 {
+  template <typename T>
+  void tf() {
+    template for (T x : {}) g(x);
+    template for (constexpr T x : {}) g(x);
+    template for (U x : {}) g(x);
+    template for (constexpr U x : {}) g(x);
+    template for (auto x : {}) g(x);
+    template for (constexpr auto x : {}) g(x);
+    template for (T x : {1, 2}) g(x);
+    template for (U x : {3, 4}) g(x);
+    template for (U x : {T(5), T(6)}) g(x);
+    template for (T x : {U(7), U(8)}) g(x);
+    template for (auto x : {T(9), T(10)}) g(x);
+    template for (auto x : {U(11), T(12)}) g(x);
+    template for (constexpr U x : {T(13), T(14)}) static_assert(tg<x>());
+    template for (constexpr T x : {U(15), U(16)}) static_assert(tg<x>());
+    template for (constexpr auto x : {T(17), U(18)}) static_assert(tg<x>());
+  }
+};
+
+template <typename T>
+void t2() {
+  template for (T x : {}) g(x);
+}
+
+void f6() {
+  t1<int>();
+  t1<long>();
+  s1<long>().tf<long>();
+  s1<int>().tf<int>();
+  s1<int>().tf<long>();
+  s1<long>().tf<int>();
+  t2<S>();
+  t2<S[1231]>();
+  t2<S***>();
+}
+
+struct X {
+  int a, b, c;
+};
+
+template <typename ...Ts>
+void t3(Ts... ts) {
+  template for (auto x : {ts...}) g(x);
+  template for (auto x : {1, ts..., 2, ts..., 3}) g(x);
+  template for (auto x : {4, ts..., ts..., 5}) g(x);
+  template for (X x : {{ts...}, {ts...}, {6, 7, 8}}) g(x.a);
+  template for (X x : {X{ts...}}) g(x.a);
+}
+
+template <int ...is>
+void t4() {
+  template for (constexpr auto x : {is...}) {
+    g(x);
+    tg<x>();
+  }
+
+  template for (constexpr auto x : {1, is..., 2, is..., 3}) {
+    g(x);
+    tg<x>();
+  }
+
+  template for (constexpr auto x : {4, is..., is..., 5}) {
+    g(x);
+    tg<x>();
+  }
+
+  template for (constexpr X x : {{is...}, {is...}, {6, 7, 8}}) {
+    g(x.a);
+    tg<x.a>();
+  }
+
+  template for (constexpr X x : {X{is...}}) {
+    g(x.a);
+    tg<x.a>();
+  }
+}
+
+template <int ...is>
+struct s2 {
+  template <int ...js>
+  void tf() {
+    template for (auto x : {is..., js...}) g(x);
+    template for (X x : {{is...}, {js...}}) g(x.a);
+    template for (constexpr auto x : {is..., js...}) tg<x>();
+    template for (constexpr X x : {{is...}, {js...}}) tg<x.a>();
+  }
+};
+
+void f7() {
+  t3(42, 43, 44);
+  t4<42, 43, 44>();
+  s2<1, 2, 3>().tf<4, 5, 6>();
+}
+
+template <int ...is>
+void t5() {
+  ([] {
+    template for (constexpr auto x : {is}) {
+      g(x);
+      tg<x>();
+    }
+  }(), ...);
+}
+
+void f8() {
+  t5<1, 2, 3>();
+}
+
+int references_enumerating() {
+  int x = 1, y = 2, z = 3;
+  template for (auto& v : {x, y, z}) { ++v; }
+  template for (auto&& v : {x, y, z}) { ++v; }
+  return x + y + z;
+}
+
+// CHECK-LABEL: define {{.*}} void @_Z2f1v()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %x3 = alloca i32, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 2, ptr %x1, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %x1, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.next2
+// CHECK: expand.next2:
+// CHECK-NEXT:   store i32 3, ptr %x3, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x3, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f2v()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca ptr, align 8
+// CHECK-NEXT:   %x3 = alloca %struct.S, align 4
+// CHECK-NEXT:   %agg.tmp = alloca %struct.S, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store ptr @.str, ptr %x1, align 8
+// CHECK-NEXT:   %1 = load ptr, ptr %x1, align 8
+// CHECK-NEXT:   call void @_Z1gPKc(ptr {{.*}} %1)
+// CHECK-NEXT:   br label %expand.next2
+// CHECK: expand.next2:
+// CHECK-NEXT:   call void @_ZN1SC1Ei(ptr {{.*}} %x3, i32 {{.*}} 45)
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %x3, i64 4, i1 false)
+// CHECK-NEXT:   %coerce.dive = getelementptr inbounds nuw %struct.S, ptr %agg.tmp, i32 0, i32 0
+// CHECK-NEXT:   %2 = load i32, ptr %coerce.dive, align 4
+// CHECK-NEXT:   call void @_Z1g1S(i32 %2)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f3v()
+// CHECK: entry:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f4v()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %y = alloca i32, align 4
+// CHECK-NEXT:   %y1 = alloca i32, align 4
+// CHECK-NEXT:   %x3 = alloca i32, align 4
+// CHECK-NEXT:   %y4 = alloca i32, align 4
+// CHECK-NEXT:   %y6 = alloca i32, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   store i32 3, ptr %y, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %y, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} %0, i32 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 4, ptr %y1, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %y1, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} %2, i32 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   br label %expand.next2
+// CHECK: expand.next2:
+// CHECK-NEXT:   store i32 2, ptr %x3, align 4
+// CHECK-NEXT:   store i32 3, ptr %y4, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x3, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %y4, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} %4, i32 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT:   store i32 4, ptr %y6, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x3, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %y6, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} %6, i32 {{.*}} %7)
+// CHECK-NEXT:   br label %expand.end7
+// CHECK: expand.end7:
+// CHECK-NEXT:   br label %expand.end8
+// CHECK: expand.end8:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f5v()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %x2 = alloca i32, align 4
+// CHECK-NEXT:   %x4 = alloca i32, align 4
+// CHECK-NEXT:   %x6 = alloca i32, align 4
+// CHECK-NEXT:   %x8 = alloca i32, align 4
+// CHECK-NEXT:   %x10 = alloca i32, align 4
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK-NEXT:   %x14 = alloca i32, align 4
+// CHECK-NEXT:   %x16 = alloca i32, align 4
+// CHECK-NEXT:   %x18 = alloca i32, align 4
+// CHECK-NEXT:   %x20 = alloca i32, align 4
+// CHECK-NEXT:   %x22 = alloca i32, align 4
+// CHECK-NEXT:   %x24 = alloca i32, align 4
+// CHECK-NEXT:   %x26 = alloca i32, align 4
+// CHECK-NEXT:   %x28 = alloca %struct.S, align 4
+// CHECK-NEXT:   %x31 = alloca %struct.S, align 4
+// CHECK-NEXT:   %x34 = alloca %struct.S, align 4
+// CHECK-NEXT:   %x37 = alloca %struct.S, align 4
+// CHECK-NEXT:   %x40 = alloca %struct.S, align 4
+// CHECK-NEXT:   %x43 = alloca %struct.S, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i32 2, ptr %x1, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %x1, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 3, ptr %x2, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x2, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   store i32 4, ptr %x4, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x4, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.end5
+// CHECK: expand.end5:
+// CHECK-NEXT:   store i32 5, ptr %x6, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 5)
+// CHECK-NEXT:   br label %expand.end7
+// CHECK: expand.end7:
+// CHECK-NEXT:   store i32 6, ptr %x8, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 6)
+// CHECK-NEXT:   br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT:   store i32 7, ptr %x10, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 7)
+// CHECK-NEXT:   br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT:   store i32 8, ptr %x12, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 8)
+// CHECK-NEXT:   br label %expand.end13
+// CHECK: expand.end13:
+// CHECK-NEXT:   store i32 9, ptr %x14, align 4
+// CHECK-NEXT:   %call = call {{.*}} i32 @_Z2tgILi9EEiv()
+// CHECK-NEXT:   br label %expand.end15
+// CHECK: expand.end15:
+// CHECK-NEXT:   store i32 10, ptr %x16, align 4
+// CHECK-NEXT:   br label %expand.next17
+// CHECK: expand.next17:
+// CHECK-NEXT:   store i32 11, ptr %x18, align 4
+// CHECK-NEXT:   br label %expand.next19
+// CHECK: expand.next19:
+// CHECK-NEXT:   store i32 12, ptr %x20, align 4
+// CHECK-NEXT:   br label %expand.end21
+// CHECK: expand.end21:
+// CHECK-NEXT:   store i32 13, ptr %x22, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x22, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT:   br label %expand.next23
+// CHECK: expand.next23:
+// CHECK-NEXT:   store i32 14, ptr %x24, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %x24, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.next25
+// CHECK: expand.next25:
+// CHECK-NEXT:   store i32 15, ptr %x26, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x26, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT:   br label %expand.end27
+// CHECK: expand.end27:
+// CHECK-NEXT:   call void @_ZN1SC1Ei(ptr {{.*}} %x28, i32 {{.*}} 16)
+// CHECK-NEXT:   %x29 = getelementptr inbounds nuw %struct.S, ptr %x28, i32 0, i32 0
+// CHECK-NEXT:   %7 = load i32, ptr %x29, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %7)
+// CHECK-NEXT:   br label %expand.next30
+// CHECK: expand.next30:
+// CHECK-NEXT:   call void @_ZN1SC1Ei(ptr {{.*}} %x31, i32 {{.*}} 17)
+// CHECK-NEXT:   %x32 = getelementptr inbounds nuw %struct.S, ptr %x31, i32 0, i32 0
+// CHECK-NEXT:   %8 = load i32, ptr %x32, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %8)
+// CHECK-NEXT:   br label %expand.next33
+// CHECK: expand.next33:
+// CHECK-NEXT:   call void @_ZN1SC1Ei(ptr {{.*}} %x34, i32 {{.*}} 18)
+// CHECK-NEXT:   %x35 = getelementptr inbounds nuw %struct.S, ptr %x34, i32 0, i32 0
+// CHECK-NEXT:   %9 = load i32, ptr %x35, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %9)
+// CHECK-NEXT:   br label %expand.end36
+// CHECK: expand.end36:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x37, ptr align 4 @__const._Z2f5v.x, i64 4, i1 false)
+// CHECK-NEXT:   %call38 = call {{.*}} i32 @_Z2tgILi19EEiv()
+// CHECK-NEXT:   br label %expand.next39
+// CHECK: expand.next39:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x40, ptr align 4 @__const._Z2f5v.x.1, i64 4, i1 false)
+// CHECK-NEXT:   %call41 = call {{.*}} i32 @_Z2tgILi20EEiv()
+// CHECK-NEXT:   br label %expand.next42
+// CHECK: expand.next42:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x43, ptr align 4 @__const._Z2f5v.x.2, i64 4, i1 false)
+// CHECK-NEXT:   %call44 = call {{.*}} i32 @_Z2tgILi21EEiv()
+// CHECK-NEXT:   br label %expand.end45
+// CHECK: expand.end45:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f6v()
+// CHECK: entry:
+// CHECK-NEXT:   %ref.tmp = alloca %struct.s1, align 1
+// CHECK-NEXT:   %ref.tmp1 = alloca %struct.s1.0, align 1
+// CHECK-NEXT:   %ref.tmp2 = alloca %struct.s1.0, align 1
+// CHECK-NEXT:   %ref.tmp3 = alloca %struct.s1, align 1
+// CHECK-NEXT:   call void @_Z2t1IiEvv()
+// CHECK-NEXT:   call void @_Z2t1IlEvv()
+// CHECK-NEXT:   call void @_ZN2s1IlE2tfIlEEvv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   call void @_ZN2s1IiE2tfIiEEvv(ptr {{.*}} %ref.tmp1)
+// CHECK-NEXT:   call void @_ZN2s1IiE2tfIlEEvv(ptr {{.*}} %ref.tmp2)
+// CHECK-NEXT:   call void @_ZN2s1IlE2tfIiEEvv(ptr {{.*}} %ref.tmp3)
+// CHECK-NEXT:   call void @_Z2t2I1SEvv()
+// CHECK-NEXT:   call void @_Z2t2IA1231_1SEvv()
+// CHECK-NEXT:   call void @_Z2t2IPPP1SEvv()
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2t1IiEvv()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %x2 = alloca i32, align 4
+// CHECK-NEXT:   %x4 = alloca i32, align 4
+// CHECK-NEXT:   %x6 = alloca i32, align 4
+// CHECK-NEXT:   %x8 = alloca i32, align 4
+// CHECK-NEXT:   %x10 = alloca i32, align 4
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK-NEXT:   %x14 = alloca i32, align 4
+// CHECK-NEXT:   %x16 = alloca i32, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 2, ptr %x1, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %x1, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i32 3, ptr %x2, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x2, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   store i32 4, ptr %x4, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x4, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.end5
+// CHECK: expand.end5:
+// CHECK-NEXT:   store i32 5, ptr %x6, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x6, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT:   br label %expand.next7
+// CHECK: expand.next7:
+// CHECK-NEXT:   store i32 6, ptr %x8, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %x8, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.end9
+// CHECK: expand.end9:
+// CHECK-NEXT:   store i32 7, ptr %x10, align 4
+// CHECK-NEXT:   br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT:   store i32 8, ptr %x12, align 4
+// CHECK-NEXT:   br label %expand.end13
+// CHECK: expand.end13:
+// CHECK-NEXT:   store i32 9, ptr %x14, align 4
+// CHECK-NEXT:   br label %expand.next15
+// CHECK: expand.next15:
+// CHECK-NEXT:   store i32 10, ptr %x16, align 4
+// CHECK-NEXT:   br label %expand.end17
+// CHECK: expand.end17:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2t1IlEvv()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i64, align 8
+// CHECK-NEXT:   %x1 = alloca i64, align 8
+// CHECK-NEXT:   %x2 = alloca i64, align 8
+// CHECK-NEXT:   %x4 = alloca i64, align 8
+// CHECK-NEXT:   %x6 = alloca i64, align 8
+// CHECK-NEXT:   %x8 = alloca i64, align 8
+// CHECK-NEXT:   %x10 = alloca i64, align 8
+// CHECK-NEXT:   %x12 = alloca i64, align 8
+// CHECK-NEXT:   %x14 = alloca i64, align 8
+// CHECK-NEXT:   %x16 = alloca i64, align 8
+// CHECK-NEXT:   store i64 1, ptr %x, align 8
+// CHECK-NEXT:   %0 = load i64, ptr %x, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i64 2, ptr %x1, align 8
+// CHECK-NEXT:   %1 = load i64, ptr %x1, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i64 3, ptr %x2, align 8
+// CHECK-NEXT:   %2 = load i64, ptr %x2, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %2)
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   store i64 4, ptr %x4, align 8
+// CHECK-NEXT:   %3 = load i64, ptr %x4, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.end5
+// CHECK: expand.end5:
+// CHECK-NEXT:   store i64 5, ptr %x6, align 8
+// CHECK-NEXT:   %4 = load i64, ptr %x6, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %4)
+// CHECK-NEXT:   br label %expand.next7
+// CHECK: expand.next7:
+// CHECK-NEXT:   store i64 6, ptr %x8, align 8
+// CHECK-NEXT:   %5 = load i64, ptr %x8, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.end9
+// CHECK: expand.end9:
+// CHECK-NEXT:   store i64 7, ptr %x10, align 8
+// CHECK-NEXT:   br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT:   store i64 8, ptr %x12, align 8
+// CHECK-NEXT:   br label %expand.end13
+// CHECK: expand.end13:
+// CHECK-NEXT:   store i64 9, ptr %x14, align 8
+// CHECK-NEXT:   br label %expand.next15
+// CHECK: expand.next15:
+// CHECK-NEXT:   store i64 10, ptr %x16, align 8
+// CHECK-NEXT:   br label %expand.end17
+// CHECK: expand.end17:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s1IlE2tfIlEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i64, align 8
+// CHECK-NEXT:   %x2 = alloca i64, align 8
+// CHECK-NEXT:   %x3 = alloca i64, align 8
+// CHECK-NEXT:   %x5 = alloca i64, align 8
+// CHECK-NEXT:   %x7 = alloca i64, align 8
+// CHECK-NEXT:   %x9 = alloca i64, align 8
+// CHECK-NEXT:   %x11 = alloca i64, align 8
+// CHECK-NEXT:   %x13 = alloca i64, align 8
+// CHECK-NEXT:   %x15 = alloca i64, align 8
+// CHECK-NEXT:   %x17 = alloca i64, align 8
+// CHECK-NEXT:   %x19 = alloca i64, align 8
+// CHECK-NEXT:   %x21 = alloca i64, align 8
+// CHECK-NEXT:   %x23 = alloca i64, align 8
+// CHECK-NEXT:   %x25 = alloca i64, align 8
+// CHECK-NEXT:   %x27 = alloca i64, align 8
+// CHECK-NEXT:   %x29 = alloca i64, align 8
+// CHECK-NEXT:   %x31 = alloca i64, align 8
+// CHECK-NEXT:   %x33 = alloca i64, align 8
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   store i64 1, ptr %x, align 8
+// CHECK-NEXT:   %0 = load i64, ptr %x, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i64 2, ptr %x2, align 8
+// CHECK-NEXT:   %1 = load i64, ptr %x2, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i64 3, ptr %x3, align 8
+// CHECK-NEXT:   %2 = load i64, ptr %x3, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %2)
+// CHECK-NEXT:   br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT:   store i64 4, ptr %x5, align 8
+// CHECK-NEXT:   %3 = load i64, ptr %x5, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.end6
+// CHECK: expand.end6:
+// CHECK-NEXT:   store i64 5, ptr %x7, align 8
+// CHECK-NEXT:   %4 = load i64, ptr %x7, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %4)
+// CHECK-NEXT:   br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT:   store i64 6, ptr %x9, align 8
+// CHECK-NEXT:   %5 = load i64, ptr %x9, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.end10
+// CHECK: expand.end10:
+// CHECK-NEXT:   store i64 7, ptr %x11, align 8
+// CHECK-NEXT:   %6 = load i64, ptr %x11, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %6)
+// CHECK-NEXT:   br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT:   store i64 8, ptr %x13, align 8
+// CHECK-NEXT:   %7 = load i64, ptr %x13, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %7)
+// CHECK-NEXT:   br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT:   store i64 9, ptr %x15, align 8
+// CHECK-NEXT:   %8 = load i64, ptr %x15, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %8)
+// CHECK-NEXT:   br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT:   store i64 10, ptr %x17, align 8
+// CHECK-NEXT:   %9 = load i64, ptr %x17, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %9)
+// CHECK-NEXT:   br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT:   store i64 11, ptr %x19, align 8
+// CHECK-NEXT:   %10 = load i64, ptr %x19, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %10)
+// CHECK-NEXT:   br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT:   store i64 12, ptr %x21, align 8
+// CHECK-NEXT:   %11 = load i64, ptr %x21, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %11)
+// CHECK-NEXT:   br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT:   store i64 13, ptr %x23, align 8
+// CHECK-NEXT:   br label %expand.next24
+// CHECK: expand.next24:
+// CHECK-NEXT:   store i64 14, ptr %x25, align 8
+// CHECK-NEXT:   br label %expand.end26
+// CHECK: expand.end26:
+// CHECK-NEXT:   store i64 15, ptr %x27, align 8
+// CHECK-NEXT:   br label %expand.next28
+// CHECK: expand.next28:
+// CHECK-NEXT:   store i64 16, ptr %x29, align 8
+// CHECK-NEXT:   br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT:   store i64 17, ptr %x31, align 8
+// CHECK-NEXT:   br label %expand.next32
+// CHECK: expand.next32:
+// CHECK-NEXT:   store i64 18, ptr %x33, align 8
+// CHECK-NEXT:   br label %expand.end34
+// CHECK: expand.end34:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s1IiE2tfIiEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x2 = alloca i32, align 4
+// CHECK-NEXT:   %x3 = alloca i32, align 4
+// CHECK-NEXT:   %x5 = alloca i32, align 4
+// CHECK-NEXT:   %x7 = alloca i32, align 4
+// CHECK-NEXT:   %x9 = alloca i32, align 4
+// CHECK-NEXT:   %x11 = alloca i32, align 4
+// CHECK-NEXT:   %x13 = alloca i32, align 4
+// CHECK-NEXT:   %x15 = alloca i32, align 4
+// CHECK-NEXT:   %x17 = alloca i32, align 4
+// CHECK-NEXT:   %x19 = alloca i32, align 4
+// CHECK-NEXT:   %x21 = alloca i32, align 4
+// CHECK-NEXT:   %x23 = alloca i32, align 4
+// CHECK-NEXT:   %x25 = alloca i32, align 4
+// CHECK-NEXT:   %x27 = alloca i32, align 4
+// CHECK-NEXT:   %x29 = alloca i32, align 4
+// CHECK-NEXT:   %x31 = alloca i32, align 4
+// CHECK-NEXT:   %x33 = alloca i32, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 2, ptr %x2, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %x2, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i32 3, ptr %x3, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x3, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT:   br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT:   store i32 4, ptr %x5, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x5, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.end6
+// CHECK: expand.end6:
+// CHECK-NEXT:   store i32 5, ptr %x7, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x7, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT:   br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT:   store i32 6, ptr %x9, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %x9, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.end10
+// CHECK: expand.end10:
+// CHECK-NEXT:   store i32 7, ptr %x11, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x11, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT:   br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT:   store i32 8, ptr %x13, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %x13, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %7)
+// CHECK-NEXT:   br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT:   store i32 9, ptr %x15, align 4
+// CHECK-NEXT:   %8 = load i32, ptr %x15, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %8)
+// CHECK-NEXT:   br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT:   store i32 10, ptr %x17, align 4
+// CHECK-NEXT:   %9 = load i32, ptr %x17, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %9)
+// CHECK-NEXT:   br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT:   store i32 11, ptr %x19, align 4
+// CHECK-NEXT:   %10 = load i32, ptr %x19, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %10)
+// CHECK-NEXT:   br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT:   store i32 12, ptr %x21, align 4
+// CHECK-NEXT:   %11 = load i32, ptr %x21, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %11)
+// CHECK-NEXT:   br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT:   store i32 13, ptr %x23, align 4
+// CHECK-NEXT:   br label %expand.next24
+// CHECK: expand.next24:
+// CHECK-NEXT:   store i32 14, ptr %x25, align 4
+// CHECK-NEXT:   br label %expand.end26
+// CHECK: expand.end26:
+// CHECK-NEXT:   store i32 15, ptr %x27, align 4
+// CHECK-NEXT:   br label %expand.next28
+// CHECK: expand.next28:
+// CHECK-NEXT:   store i32 16, ptr %x29, align 4
+// CHECK-NEXT:   br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT:   store i32 17, ptr %x31, align 4
+// CHECK-NEXT:   br label %expand.next32
+// CHECK: expand.next32:
+// CHECK-NEXT:   store i32 18, ptr %x33, align 4
+// CHECK-NEXT:   br label %expand.end34
+// CHECK: expand.end34:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s1IiE2tfIlEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i64, align 8
+// CHECK-NEXT:   %x2 = alloca i64, align 8
+// CHECK-NEXT:   %x3 = alloca i32, align 4
+// CHECK-NEXT:   %x5 = alloca i32, align 4
+// CHECK-NEXT:   %x7 = alloca i32, align 4
+// CHECK-NEXT:   %x9 = alloca i32, align 4
+// CHECK-NEXT:   %x11 = alloca i64, align 8
+// CHECK-NEXT:   %x13 = alloca i64, align 8
+// CHECK-NEXT:   %x15 = alloca i64, align 8
+// CHECK-NEXT:   %x17 = alloca i64, align 8
+// CHECK-NEXT:   %x19 = alloca i32, align 4
+// CHECK-NEXT:   %x21 = alloca i64, align 8
+// CHECK-NEXT:   %x23 = alloca i32, align 4
+// CHECK-NEXT:   %x25 = alloca i32, align 4
+// CHECK-NEXT:   %x27 = alloca i64, align 8
+// CHECK-NEXT:   %x29 = alloca i64, align 8
+// CHECK-NEXT:   %x31 = alloca i64, align 8
+// CHECK-NEXT:   %x33 = alloca i32, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   store i64 1, ptr %x, align 8
+// CHECK-NEXT:   %0 = load i64, ptr %x, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i64 2, ptr %x2, align 8
+// CHECK-NEXT:   %1 = load i64, ptr %x2, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i32 3, ptr %x3, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x3, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT:   br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT:   store i32 4, ptr %x5, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x5, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.end6
+// CHECK: expand.end6:
+// CHECK-NEXT:   store i32 5, ptr %x7, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x7, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT:   br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT:   store i32 6, ptr %x9, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %x9, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.end10
+// CHECK: expand.end10:
+// CHECK-NEXT:   store i64 7, ptr %x11, align 8
+// CHECK-NEXT:   %6 = load i64, ptr %x11, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %6)
+// CHECK-NEXT:   br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT:   store i64 8, ptr %x13, align 8
+// CHECK-NEXT:   %7 = load i64, ptr %x13, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %7)
+// CHECK-NEXT:   br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT:   store i64 9, ptr %x15, align 8
+// CHECK-NEXT:   %8 = load i64, ptr %x15, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %8)
+// CHECK-NEXT:   br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT:   store i64 10, ptr %x17, align 8
+// CHECK-NEXT:   %9 = load i64, ptr %x17, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %9)
+// CHECK-NEXT:   br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT:   store i32 11, ptr %x19, align 4
+// CHECK-NEXT:   %10 = load i32, ptr %x19, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %10)
+// CHECK-NEXT:   br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT:   store i64 12, ptr %x21, align 8
+// CHECK-NEXT:   %11 = load i64, ptr %x21, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %11)
+// CHECK-NEXT:   br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT:   store i32 13, ptr %x23, align 4
+// CHECK-NEXT:   br label %expand.next24
+// CHECK: expand.next24:
+// CHECK-NEXT:   store i32 14, ptr %x25, align 4
+// CHECK-NEXT:   br label %expand.end26
+// CHECK: expand.end26:
+// CHECK-NEXT:   store i64 15, ptr %x27, align 8
+// CHECK-NEXT:   br label %expand.next28
+// CHECK: expand.next28:
+// CHECK-NEXT:   store i64 16, ptr %x29, align 8
+// CHECK-NEXT:   br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT:   store i64 17, ptr %x31, align 8
+// CHECK-NEXT:   br label %expand.next32
+// CHECK: expand.next32:
+// CHECK-NEXT:   store i32 18, ptr %x33, align 4
+// CHECK-NEXT:   br label %expand.end34
+// CHECK: expand.end34:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s1IlE2tfIiEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x2 = alloca i32, align 4
+// CHECK-NEXT:   %x3 = alloca i64, align 8
+// CHECK-NEXT:   %x5 = alloca i64, align 8
+// CHECK-NEXT:   %x7 = alloca i64, align 8
+// CHECK-NEXT:   %x9 = alloca i64, align 8
+// CHECK-NEXT:   %x11 = alloca i32, align 4
+// CHECK-NEXT:   %x13 = alloca i32, align 4
+// CHECK-NEXT:   %x15 = alloca i32, align 4
+// CHECK-NEXT:   %x17 = alloca i32, align 4
+// CHECK-NEXT:   %x19 = alloca i64, align 8
+// CHECK-NEXT:   %x21 = alloca i32, align 4
+// CHECK-NEXT:   %x23 = alloca i64, align 8
+// CHECK-NEXT:   %x25 = alloca i64, align 8
+// CHECK-NEXT:   %x27 = alloca i32, align 4
+// CHECK-NEXT:   %x29 = alloca i32, align 4
+// CHECK-NEXT:   %x31 = alloca i32, align 4
+// CHECK-NEXT:   %x33 = alloca i64, align 8
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 2, ptr %x2, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %x2, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i64 3, ptr %x3, align 8
+// CHECK-NEXT:   %2 = load i64, ptr %x3, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %2)
+// CHECK-NEXT:   br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT:   store i64 4, ptr %x5, align 8
+// CHECK-NEXT:   %3 = load i64, ptr %x5, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.end6
+// CHECK: expand.end6:
+// CHECK-NEXT:   store i64 5, ptr %x7, align 8
+// CHECK-NEXT:   %4 = load i64, ptr %x7, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %4)
+// CHECK-NEXT:   br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT:   store i64 6, ptr %x9, align 8
+// CHECK-NEXT:   %5 = load i64, ptr %x9, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.end10
+// CHECK: expand.end10:
+// CHECK-NEXT:   store i32 7, ptr %x11, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x11, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT:   br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT:   store i32 8, ptr %x13, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %x13, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %7)
+// CHECK-NEXT:   br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT:   store i32 9, ptr %x15, align 4
+// CHECK-NEXT:   %8 = load i32, ptr %x15, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %8)
+// CHECK-NEXT:   br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT:   store i32 10, ptr %x17, align 4
+// CHECK-NEXT:   %9 = load i32, ptr %x17, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %9)
+// CHECK-NEXT:   br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT:   store i64 11, ptr %x19, align 8
+// CHECK-NEXT:   %10 = load i64, ptr %x19, align 8
+// CHECK-NEXT:   call void @_Z1gl(i64 {{.*}} %10)
+// CHECK-NEXT:   br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT:   store i32 12, ptr %x21, align 4
+// CHECK-NEXT:   %11 = load i32, ptr %x21, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %11)
+// CHECK-NEXT:   br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT:   store i64 13, ptr %x23, align 8
+// CHECK-NEXT:   br label %expand.next24
+// CHECK: expand.next24:
+// CHECK-NEXT:   store i64 14, ptr %x25, align 8
+// CHECK-NEXT:   br label %expand.end26
+// CHECK: expand.end26:
+// CHECK-NEXT:   store i32 15, ptr %x27, align 4
+// CHECK-NEXT:   br label %expand.next28
+// CHECK: expand.next28:
+// CHECK-NEXT:   store i32 16, ptr %x29, align 4
+// CHECK-NEXT:   br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT:   store i32 17, ptr %x31, align 4
+// CHECK-NEXT:   br label %expand.next32
+// CHECK: expand.next32:
+// CHECK-NEXT:   store i64 18, ptr %x33, align 8
+// CHECK-NEXT:   br label %expand.end34
+// CHECK: expand.end34:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f7v()
+// CHECK: entry:
+// CHECK-NEXT:   %ref.tmp = alloca %struct.s2, align 1
+// CHECK-NEXT:   call void @_Z2t3IJiiiEEvDpT_(i32 {{.*}} 42, i32 {{.*}} 43, i32 {{.*}} 44)
+// CHECK-NEXT:   call void @_Z2t4IJLi42ELi43ELi44EEEvv()
+// CHECK-NEXT:   call void @_ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   ret void
+
+// CHECK-LABEL: define {{.*}} void @_Z2t3IJiiiEEvDpT_(i32 {{.*}} %ts, i32 {{.*}} %ts1, i32 {{.*}} %ts3)
+// CHECK: entry:
+// CHECK-NEXT:   %ts.addr = alloca i32, align 4
+// CHECK-NEXT:   %ts.addr2 = alloca i32, align 4
+// CHECK-NEXT:   %ts.addr4 = alloca i32, align 4
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x5 = alloca i32, align 4
+// CHECK-NEXT:   %x7 = alloca i32, align 4
+// CHECK-NEXT:   %x8 = alloca i32, align 4
+// CHECK-NEXT:   %x10 = alloca i32, align 4
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK-NEXT:   %x14 = alloca i32, align 4
+// CHECK-NEXT:   %x16 = alloca i32, align 4
+// CHECK-NEXT:   %x18 = alloca i32, align 4
+// CHECK-NEXT:   %x20 = alloca i32, align 4
+// CHECK-NEXT:   %x22 = alloca i32, align 4
+// CHECK-NEXT:   %x24 = alloca i32, align 4
+// CHECK-NEXT:   %x26 = alloca i32, align 4
+// CHECK-NEXT:   %x28 = alloca i32, align 4
+// CHECK-NEXT:   %x30 = alloca i32, align 4
+// CHECK-NEXT:   %x32 = alloca i32, align 4
+// CHECK-NEXT:   %x34 = alloca i32, align 4
+// CHECK-NEXT:   %x36 = alloca i32, align 4
+// CHECK-NEXT:   %x38 = alloca i32, align 4
+// CHECK-NEXT:   %x40 = alloca i32, align 4
+// CHECK-NEXT:   %x42 = alloca %struct.X, align 4
+// CHECK-NEXT:   %x45 = alloca %struct.X, align 4
+// CHECK-NEXT:   %x51 = alloca %struct.X, align 4
+// CHECK-NEXT:   %x54 = alloca %struct.X, align 4
+// CHECK-NEXT:   store i32 %ts, ptr %ts.addr, align 4
+// CHECK-NEXT:   store i32 %ts1, ptr %ts.addr2, align 4
+// CHECK-NEXT:   store i32 %ts3, ptr %ts.addr4, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT:   store i32 %0, ptr %x, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %2 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT:   store i32 %2, ptr %x5, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x5, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.next6
+// CHECK: expand.next6:
+// CHECK-NEXT:   %4 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT:   store i32 %4, ptr %x7, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %x7, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i32 1, ptr %x8, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x8, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT:   br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT:   %7 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT:   store i32 %7, ptr %x10, align 4
+// CHECK-NEXT:   %8 = load i32, ptr %x10, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %8)
+// CHECK-NEXT:   br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT:   %9 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT:   store i32 %9, ptr %x12, align 4
+// CHECK-NEXT:   %10 = load i32, ptr %x12, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %10)
+// CHECK-NEXT:   br label %expand.next13
+// CHECK: expand.next13:
+// CHECK-NEXT:   %11 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT:   store i32 %11, ptr %x14, align 4
+// CHECK-NEXT:   %12 = load i32, ptr %x14, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %12)
+// CHECK-NEXT:   br label %expand.next15
+// CHECK: expand.next15:
+// CHECK-NEXT:   store i32 2, ptr %x16, align 4
+// CHECK-NEXT:   %13 = load i32, ptr %x16, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %13)
+// CHECK-NEXT:   br label %expand.next17
+// CHECK: expand.next17:
+// CHECK-NEXT:   %14 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT:   store i32 %14, ptr %x18, align 4
+// CHECK-NEXT:   %15 = load i32, ptr %x18, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %15)
+// CHECK-NEXT:   br label %expand.next19
+// CHECK: expand.next19:
+// CHECK-NEXT:   %16 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT:   store i32 %16, ptr %x20, align 4
+// CHECK-NEXT:   %17 = load i32, ptr %x20, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %17)
+// CHECK-NEXT:   br label %expand.next21
+// CHECK: expand.next21:
+// CHECK-NEXT:   %18 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT:   store i32 %18, ptr %x22, align 4
+// CHECK-NEXT:   %19 = load i32, ptr %x22, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %19)
+// CHECK-NEXT:   br label %expand.next23
+// CHECK: expand.next23:
+// CHECK-NEXT:   store i32 3, ptr %x24, align 4
+// CHECK-NEXT:   %20 = load i32, ptr %x24, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %20)
+// CHECK-NEXT:   br label %expand.end25
+// CHECK: expand.end25:
+// CHECK-NEXT:   store i32 4, ptr %x26, align 4
+// CHECK-NEXT:   %21 = load i32, ptr %x26, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %21)
+// CHECK-NEXT:   br label %expand.next27
+// CHECK: expand.next27:
+// CHECK-NEXT:   %22 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT:   store i32 %22, ptr %x28, align 4
+// CHECK-NEXT:   %23 = load i32, ptr %x28, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %23)
+// CHECK-NEXT:   br label %expand.next29
+// CHECK: expand.next29:
+// CHECK-NEXT:   %24 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT:   store i32 %24, ptr %x30, align 4
+// CHECK-NEXT:   %25 = load i32, ptr %x30, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %25)
+// CHECK-NEXT:   br label %expand.next31
+// CHECK: expand.next31:
+// CHECK-NEXT:   %26 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT:   store i32 %26, ptr %x32, align 4
+// CHECK-NEXT:   %27 = load i32, ptr %x32, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %27)
+// CHECK-NEXT:   br label %expand.next33
+// CHECK: expand.next33:
+// CHECK-NEXT:   %28 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT:   store i32 %28, ptr %x34, align 4
+// CHECK-NEXT:   %29 = load i32, ptr %x34, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %29)
+// CHECK-NEXT:   br label %expand.next35
+// CHECK: expand.next35:
+// CHECK-NEXT:   %30 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT:   store i32 %30, ptr %x36, align 4
+// CHECK-NEXT:   %31 = load i32, ptr %x36, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %31)
+// CHECK-NEXT:   br label %expand.next37
+// CHECK: expand.next37:
+// CHECK-NEXT:   %32 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT:   store i32 %32, ptr %x38, align 4
+// CHECK-NEXT:   %33 = load i32, ptr %x38, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %33)
+// CHECK-NEXT:   br label %expand.next39
+// CHECK: expand.next39:
+// CHECK-NEXT:   store i32 5, ptr %x40, align 4
+// CHECK-NEXT:   %34 = load i32, ptr %x40, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %34)
+// CHECK-NEXT:   br label %expand.end41
+// CHECK: expand.end41:
+// CHECK-NEXT:   %a = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 0
+// CHECK-NEXT:   %35 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT:   store i32 %35, ptr %a, align 4
+// CHECK-NEXT:   %b = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 1
+// CHECK-NEXT:   %36 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT:   store i32 %36, ptr %b, align 4
+// CHECK-NEXT:   %c = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 2
+// CHECK-NEXT:   %37 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT:   store i32 %37, ptr %c, align 4
+// CHECK-NEXT:   %a43 = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 0
+// CHECK-NEXT:   %38 = load i32, ptr %a43, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %38)
+// CHECK-NEXT:   br label %expand.next44
+// CHECK: expand.next44:
+// CHECK-NEXT:   %a46 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 0
+// CHECK-NEXT:   %39 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT:   store i32 %39, ptr %a46, align 4
+// CHECK-NEXT:   %b47 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 1
+// CHECK-NEXT:   %40 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT:   store i32 %40, ptr %b47, align 4
+// CHECK-NEXT:   %c48 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 2
+// CHECK-NEXT:   %41 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT:   store i32 %41, ptr %c48, align 4
+// CHECK-NEXT:   %a49 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 0
+// CHECK-NEXT:   %42 = load i32, ptr %a49, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %42)
+// CHECK-NEXT:   br label %expand.next50
+// CHECK: expand.next50:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x51, ptr align 4 @__const._Z2t3IJiiiEEvDpT_.x, i64 12, i1 false)
+// CHECK-NEXT:   %a52 = getelementptr inbounds nuw %struct.X, ptr %x51, i32 0, i32 0
+// CHECK-NEXT:   %43 = load i32, ptr %a52, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %43)
+// CHECK-NEXT:   br label %expand.end53
+// CHECK: expand.end53:
+// CHECK-NEXT:   %a55 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 0
+// CHECK-NEXT:   %44 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT:   store i32 %44, ptr %a55, align 4
+// CHECK-NEXT:   %b56 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 1
+// CHECK-NEXT:   %45 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT:   store i32 %45, ptr %b56, align 4
+// CHECK-NEXT:   %c57 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 2
+// CHECK-NEXT:   %46 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT:   store i32 %46, ptr %c57, align 4
+// CHECK-NEXT:   %a58 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 0
+// CHECK-NEXT:   %47 = load i32, ptr %a58, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %47)
+// CHECK-NEXT:   br label %expand.end59
+// CHECK: expand.end59:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2t4IJLi42ELi43ELi44EEEvv()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %x4 = alloca i32, align 4
+// CHECK-NEXT:   %x6 = alloca i32, align 4
+// CHECK-NEXT:   %x9 = alloca i32, align 4
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK-NEXT:   %x15 = alloca i32, align 4
+// CHECK-NEXT:   %x18 = alloca i32, align 4
+// CHECK-NEXT:   %x21 = alloca i32, align 4
+// CHECK-NEXT:   %x24 = alloca i32, align 4
+// CHECK-NEXT:   %x27 = alloca i32, align 4
+// CHECK-NEXT:   %x30 = alloca i32, align 4
+// CHECK-NEXT:   %x33 = alloca i32, align 4
+// CHECK-NEXT:   %x36 = alloca i32, align 4
+// CHECK-NEXT:   %x39 = alloca i32, align 4
+// CHECK-NEXT:   %x42 = alloca i32, align 4
+// CHECK-NEXT:   %x45 = alloca i32, align 4
+// CHECK-NEXT:   %x48 = alloca i32, align 4
+// CHECK-NEXT:   %x51 = alloca i32, align 4
+// CHECK-NEXT:   %x54 = alloca i32, align 4
+// CHECK-NEXT:   %x57 = alloca %struct.X, align 4
+// CHECK-NEXT:   %x60 = alloca %struct.X, align 4
+// CHECK-NEXT:   %x63 = alloca %struct.X, align 4
+// CHECK-NEXT:   %x66 = alloca %struct.X, align 4
+// CHECK-NEXT:   store i32 42, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT:   %call = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 43, ptr %x1, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT:   %call2 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   store i32 44, ptr %x4, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT:   %call5 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i32 1, ptr %x6, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 1)
+// CHECK-NEXT:   %call7 = call {{.*}} i32 @_Z2tgILi1EEiv()
+// CHECK-NEXT:   br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT:   store i32 42, ptr %x9, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT:   %call10 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT:   br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT:   store i32 43, ptr %x12, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT:   %call13 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT:   br label %expand.next14
+// CHECK: expand.next14:
+// CHECK-NEXT:   store i32 44, ptr %x15, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT:   %call16 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT:   br label %expand.next17
+// CHECK: expand.next17:
+// CHECK-NEXT:   store i32 2, ptr %x18, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 2)
+// CHECK-NEXT:   %call19 = call {{.*}} i32 @_Z2tgILi2EEiv()
+// CHECK-NEXT:   br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT:   store i32 42, ptr %x21, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT:   %call22 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT:   br label %expand.next23
+// CHECK: expand.next23:
+// CHECK-NEXT:   store i32 43, ptr %x24, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT:   %call25 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT:   br label %expand.next26
+// CHECK: expand.next26:
+// CHECK-NEXT:   store i32 44, ptr %x27, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT:   %call28 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT:   br label %expand.next29
+// CHECK: expand.next29:
+// CHECK-NEXT:   store i32 3, ptr %x30, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 3)
+// CHECK-NEXT:   %call31 = call {{.*}} i32 @_Z2tgILi3EEiv()
+// CHECK-NEXT:   br label %expand.end32
+// CHECK: expand.end32:
+// CHECK-NEXT:   store i32 4, ptr %x33, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 4)
+// CHECK-NEXT:   %call34 = call {{.*}} i32 @_Z2tgILi4EEiv()
+// CHECK-NEXT:   br label %expand.next35
+// CHECK: expand.next35:
+// CHECK-NEXT:   store i32 42, ptr %x36, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT:   %call37 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT:   br label %expand.next38
+// CHECK: expand.next38:
+// CHECK-NEXT:   store i32 43, ptr %x39, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT:   %call40 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT:   br label %expand.next41
+// CHECK: expand.next41:
+// CHECK-NEXT:   store i32 44, ptr %x42, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT:   %call43 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT:   br label %expand.next44
+// CHECK: expand.next44:
+// CHECK-NEXT:   store i32 42, ptr %x45, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT:   %call46 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT:   br label %expand.next47
+// CHECK: expand.next47:
+// CHECK-NEXT:   store i32 43, ptr %x48, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT:   %call49 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT:   br label %expand.next50
+// CHECK: expand.next50:
+// CHECK-NEXT:   store i32 44, ptr %x51, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT:   %call52 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT:   br label %expand.next53
+// CHECK: expand.next53:
+// CHECK-NEXT:   store i32 5, ptr %x54, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 5)
+// CHECK-NEXT:   %call55 = call {{.*}} i32 @_Z2tgILi5EEiv()
+// CHECK-NEXT:   br label %expand.end56
+// CHECK: expand.end56:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x57, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x, i64 12, i1 false)
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT:   %call58 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT:   br label %expand.next59
+// CHECK: expand.next59:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x60, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x.3, i64 12, i1 false)
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT:   %call61 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT:   br label %expand.next62
+// CHECK: expand.next62:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x63, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x.4, i64 12, i1 false)
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 6)
+// CHECK-NEXT:   %call64 = call {{.*}} i32 @_Z2tgILi6EEiv()
+// CHECK-NEXT:   br label %expand.end65
+// CHECK: expand.end65:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x66, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x.5, i64 12, i1 false)
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT:   %call67 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT:   br label %expand.end68
+// CHECK: expand.end68:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x2 = alloca i32, align 4
+// CHECK-NEXT:   %x4 = alloca i32, align 4
+// CHECK-NEXT:   %x6 = alloca i32, align 4
+// CHECK-NEXT:   %x8 = alloca i32, align 4
+// CHECK-NEXT:   %x10 = alloca i32, align 4
+// CHECK-NEXT:   %x11 = alloca %struct.X, align 4
+// CHECK-NEXT:   %x13 = alloca %struct.X, align 4
+// CHECK-NEXT:   %x16 = alloca i32, align 4
+// CHECK-NEXT:   %x18 = alloca i32, align 4
+// CHECK-NEXT:   %x21 = alloca i32, align 4
+// CHECK-NEXT:   %x24 = alloca i32, align 4
+// CHECK-NEXT:   %x27 = alloca i32, align 4
+// CHECK-NEXT:   %x30 = alloca i32, align 4
+// CHECK-NEXT:   %x33 = alloca %struct.X, align 4
+// CHECK-NEXT:   %x36 = alloca %struct.X, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 2, ptr %x2, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %x2, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   store i32 3, ptr %x4, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x4, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT:   br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT:   store i32 4, ptr %x6, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x6, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.next7
+// CHECK: expand.next7:
+// CHECK-NEXT:   store i32 5, ptr %x8, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x8, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT:   br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT:   store i32 6, ptr %x10, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %x10, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x11, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x, i64 12, i1 false)
+// CHECK-NEXT:   %a = getelementptr inbounds nuw %struct.X, ptr %x11, i32 0, i32 0
+// CHECK-NEXT:   %6 = load i32, ptr %a, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT:   br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x13, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x.6, i64 12, i1 false)
+// CHECK-NEXT:   %a14 = getelementptr inbounds nuw %struct.X, ptr %x13, i32 0, i32 0
+// CHECK-NEXT:   %7 = load i32, ptr %a14, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} %7)
+// CHECK-NEXT:   br label %expand.end15
+// CHECK: expand.end15:
+// CHECK-NEXT:   store i32 1, ptr %x16, align 4
+// CHECK-NEXT:   %call = call {{.*}} i32 @_Z2tgILi1EEiv()
+// CHECK-NEXT:   br label %expand.next17
+// CHECK: expand.next17:
+// CHECK-NEXT:   store i32 2, ptr %x18, align 4
+// CHECK-NEXT:   %call19 = call {{.*}} i32 @_Z2tgILi2EEiv()
+// CHECK-NEXT:   br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT:   store i32 3, ptr %x21, align 4
+// CHECK-NEXT:   %call22 = call {{.*}} i32 @_Z2tgILi3EEiv()
+// CHECK-NEXT:   br label %expand.next23
+// CHECK: expand.next23:
+// CHECK-NEXT:   store i32 4, ptr %x24, align 4
+// CHECK-NEXT:   %call25 = call {{.*}} i32 @_Z2tgILi4EEiv()
+// CHECK-NEXT:   br label %expand.next26
+// CHECK: expand.next26:
+// CHECK-NEXT:   store i32 5, ptr %x27, align 4
+// CHECK-NEXT:   %call28 = call {{.*}} i32 @_Z2tgILi5EEiv()
+// CHECK-NEXT:   br label %expand.next29
+// CHECK: expand.next29:
+// CHECK-NEXT:   store i32 6, ptr %x30, align 4
+// CHECK-NEXT:   %call31 = call {{.*}} i32 @_Z2tgILi6EEiv()
+// CHECK-NEXT:   br label %expand.end32
+// CHECK: expand.end32:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x33, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x.7, i64 12, i1 false)
+// CHECK-NEXT:   %call34 = call {{.*}} i32 @_Z2tgILi1EEiv()
+// CHECK-NEXT:   br label %expand.next35
+// CHECK: expand.next35:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x36, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x.8, i64 12, i1 false)
+// CHECK-NEXT:   %call37 = call {{.*}} i32 @_Z2tgILi4EEiv()
+// CHECK-NEXT:   br label %expand.end38
+// CHECK: expand.end38:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f8v()
+// CHECK: entry:
+// CHECK-NEXT:   call void @_Z2t5IJLi1ELi2ELi3EEEvv()
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2t5IJLi1ELi2ELi3EEEvv()
+// CHECK: entry:
+// CHECK-NEXT:   %ref.tmp = alloca %class.anon, align 1
+// CHECK-NEXT:   %ref.tmp1 = alloca %class.anon.1, align 1
+// CHECK-NEXT:   %ref.tmp2 = alloca %class.anon.3, align 1
+// CHECK-NEXT:   call void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE1_clEv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   call void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE0_clEv(ptr {{.*}} %ref.tmp1)
+// CHECK-NEXT:   call void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE_clEv(ptr {{.*}} %ref.tmp2)
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z22references_enumeratingv()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %y = alloca i32, align 4
+// CHECK-NEXT:   %z = alloca i32, align 4
+// CHECK-NEXT:   %v = alloca ptr, align 8
+// CHECK-NEXT:   %v1 = alloca ptr, align 8
+// CHECK-NEXT:   %v4 = alloca ptr, align 8
+// CHECK-NEXT:   %v6 = alloca ptr, align 8
+// CHECK-NEXT:   %v9 = alloca ptr, align 8
+// CHECK-NEXT:   %v12 = alloca ptr, align 8
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   store i32 2, ptr %y, align 4
+// CHECK-NEXT:   store i32 3, ptr %z, align 4
+// CHECK-NEXT:   store ptr %x, ptr %v, align 8
+// CHECK-NEXT:   %0 = load ptr, ptr %v, align 8
+// CHECK-NEXT:   %1 = load i32, ptr %0, align 4
+// CHECK-NEXT:   %inc = add nsw i32 %1, 1
+// CHECK-NEXT:   store i32 %inc, ptr %0, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store ptr %y, ptr %v1, align 8
+// CHECK-NEXT:   %2 = load ptr, ptr %v1, align 8
+// CHECK-NEXT:   %3 = load i32, ptr %2, align 4
+// CHECK-NEXT:   %inc2 = add nsw i32 %3, 1
+// CHECK-NEXT:   store i32 %inc2, ptr %2, align 4
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   store ptr %z, ptr %v4, align 8
+// CHECK-NEXT:   %4 = load ptr, ptr %v4, align 8
+// CHECK-NEXT:   %5 = load i32, ptr %4, align 4
+// CHECK-NEXT:   %inc5 = add nsw i32 %5, 1
+// CHECK-NEXT:   store i32 %inc5, ptr %4, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store ptr %x, ptr %v6, align 8
+// CHECK-NEXT:   %6 = load ptr, ptr %v6, align 8
+// CHECK-NEXT:   %7 = load i32, ptr %6, align 4
+// CHECK-NEXT:   %inc7 = add nsw i32 %7, 1
+// CHECK-NEXT:   store i32 %inc7, ptr %6, align 4
+// CHECK-NEXT:   br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT:   store ptr %y, ptr %v9, align 8
+// CHECK-NEXT:   %8 = load ptr, ptr %v9, align 8
+// CHECK-NEXT:   %9 = load i32, ptr %8, align 4
+// CHECK-NEXT:   %inc10 = add nsw i32 %9, 1
+// CHECK-NEXT:   store i32 %inc10, ptr %8, align 4
+// CHECK-NEXT:   br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT:   store ptr %z, ptr %v12, align 8
+// CHECK-NEXT:   %10 = load ptr, ptr %v12, align 8
+// CHECK-NEXT:   %11 = load i32, ptr %10, align 4
+// CHECK-NEXT:   %inc13 = add nsw i32 %11, 1
+// CHECK-NEXT:   store i32 %inc13, ptr %10, align 4
+// CHECK-NEXT:   br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT:   %12 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %13 = load i32, ptr %y, align 4
+// CHECK-NEXT:   %add = add nsw i32 %12, %13
+// CHECK-NEXT:   %14 = load i32, ptr %z, align 4
+// CHECK-NEXT:   %add15 = add nsw i32 %add, %14
+// CHECK-NEXT:   ret i32 %add15
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE1_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 1)
+// CHECK-NEXT:   %call = call {{.*}} i32 @_Z2tgILi1EEiv()
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE0_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   store i32 2, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 2)
+// CHECK-NEXT:   %call = call {{.*}} i32 @_Z2tgILi2EEiv()
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   store i32 3, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1gi(i32 {{.*}} 3)
+// CHECK-NEXT:   %call = call {{.*}} i32 @_Z2tgILi3EEiv()
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   ret void
diff --git a/clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp
new file mode 100644
index 0000000000000..7bcaf56adf652
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp
@@ -0,0 +1,430 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+void h(int, int);
+
+void break_continue() {
+  template for (auto x : {1, 2}) {
+    break;
+    h(1, x);
+  }
+
+  template for (auto x : {3, 4}) {
+    continue;
+    h(2, x);
+  }
+
+  template for (auto x : {5, 6}) {
+    if (x == 2) break;
+    h(3, x);
+  }
+
+  template for (auto x : {7, 8}) {
+    if (x == 2) continue;
+    h(4, x);
+  }
+}
+
+int break_continue_nested() {
+  int sum = 0;
+
+  template for (auto x : {1, 2}) {
+    template for (auto y : {3, 4}) {
+      if (x == 2) break;
+      sum += y;
+    }
+    sum += x;
+  }
+
+  template for (auto x : {5, 6}) {
+    template for (auto y : {7, 8}) {
+      if (x == 6) continue;
+      sum += y;
+    }
+    sum += x;
+  }
+
+  return sum;
+}
+
+void label() {
+  // Only local labels are allowed in expansion statements.
+  template for (auto x : {1, 2, 3}) {
+    __label__ a;
+    if (x == 1) goto a;
+    h(1, x);
+    a:;
+  }
+}
+
+void nested_label() {
+  template for (auto x : {1, 2}) {
+    __label__ a;
+    template for (auto y : {3, 4}) {
+      if (y == 3) goto a;
+      if (y == 4) goto end;
+      h(x, y);
+    }
+    a:;
+  }
+  end:
+}
+
+
+// CHECK-LABEL: define {{.*}} void @_Z14break_continuev()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %x2 = alloca i32, align 4
+// CHECK-NEXT:   %x3 = alloca i32, align 4
+// CHECK-NEXT:   %x5 = alloca i32, align 4
+// CHECK-NEXT:   %x7 = alloca i32, align 4
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK-NEXT:   %x17 = alloca i32, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store i32 3, ptr %x2, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 4, ptr %x3, align 4
+// CHECK-NEXT:   br label %expand.end4
+// CHECK: expand.end4:
+// CHECK-NEXT:   store i32 5, ptr %x5, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x5, align 4
+// CHECK-NEXT:   %cmp = icmp eq i32 %0, 2
+// CHECK-NEXT:   br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT:   br label %expand.end11
+// CHECK: if.end:
+// CHECK-NEXT:   %1 = load i32, ptr %x5, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} 3, i32 {{.*}} %1)
+// CHECK-NEXT:   br label %expand.next6
+// CHECK: expand.next6:
+// CHECK-NEXT:   store i32 6, ptr %x7, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x7, align 4
+// CHECK-NEXT:   %cmp8 = icmp eq i32 %2, 2
+// CHECK-NEXT:   br i1 %cmp8, label %if.then9, label %if.end10
+// CHECK: if.then9:
+// CHECK-NEXT:   br label %expand.end11
+// CHECK: if.end10:
+// CHECK-NEXT:   %3 = load i32, ptr %x7, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} 3, i32 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.end11
+// CHECK: expand.end11:
+// CHECK-NEXT:   store i32 7, ptr %x12, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x12, align 4
+// CHECK-NEXT:   %cmp13 = icmp eq i32 %4, 2
+// CHECK-NEXT:   br i1 %cmp13, label %if.then14, label %if.end15
+// CHECK: if.then14:
+// CHECK-NEXT:   br label %expand.next16
+// CHECK: if.end15:
+// CHECK-NEXT:   %5 = load i32, ptr %x12, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} 4, i32 {{.*}} %5)
+// CHECK-NEXT:   br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT:   store i32 8, ptr %x17, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x17, align 4
+// CHECK-NEXT:   %cmp18 = icmp eq i32 %6, 2
+// CHECK-NEXT:   br i1 %cmp18, label %if.then19, label %if.end20
+// CHECK: if.then19:
+// CHECK-NEXT:   br label %expand.end21
+// CHECK: if.end20:
+// CHECK-NEXT:   %7 = load i32, ptr %x17, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} 4, i32 {{.*}} %7)
+// CHECK-NEXT:   br label %expand.end21
+// CHECK: expand.end21:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z21break_continue_nestedv()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %y = alloca i32, align 4
+// CHECK-NEXT:   %y1 = alloca i32, align 4
+// CHECK-NEXT:   %x8 = alloca i32, align 4
+// CHECK-NEXT:   %y9 = alloca i32, align 4
+// CHECK-NEXT:   %y15 = alloca i32, align 4
+// CHECK-NEXT:   %x23 = alloca i32, align 4
+// CHECK-NEXT:   %y24 = alloca i32, align 4
+// CHECK-NEXT:   %y30 = alloca i32, align 4
+// CHECK-NEXT:   %x38 = alloca i32, align 4
+// CHECK-NEXT:   %y39 = alloca i32, align 4
+// CHECK-NEXT:   %y45 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   store i32 3, ptr %y, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %cmp = icmp eq i32 %0, 2
+// CHECK-NEXT:   br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT:   br label %expand.end
+// CHECK: if.end:
+// CHECK-NEXT:   %1 = load i32, ptr %y, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %2, %1
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 4, ptr %y1, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %cmp2 = icmp eq i32 %3, 2
+// CHECK-NEXT:   br i1 %cmp2, label %if.then3, label %if.end4
+// CHECK: if.then3:
+// CHECK-NEXT:   br label %expand.end
+// CHECK: if.end4:
+// CHECK-NEXT:   %4 = load i32, ptr %y1, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add5 = add nsw i32 %5, %4
+// CHECK-NEXT:   store i32 %add5, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   %6 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add6 = add nsw i32 %7, %6
+// CHECK-NEXT:   store i32 %add6, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next7
+// CHECK: expand.next7:
+// CHECK-NEXT:   store i32 2, ptr %x8, align 4
+// CHECK-NEXT:   store i32 3, ptr %y9, align 4
+// CHECK-NEXT:   %8 = load i32, ptr %x8, align 4
+// CHECK-NEXT:   %cmp10 = icmp eq i32 %8, 2
+// CHECK-NEXT:   br i1 %cmp10, label %if.then11, label %if.end12
+// CHECK: if.then11:
+// CHECK-NEXT:   br label %expand.end20
+// CHECK: if.end12:
+// CHECK-NEXT:   %9 = load i32, ptr %y9, align 4
+// CHECK-NEXT:   %10 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add13 = add nsw i32 %10, %9
+// CHECK-NEXT:   store i32 %add13, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next14
+// CHECK: expand.next14:
+// CHECK-NEXT:   store i32 4, ptr %y15, align 4
+// CHECK-NEXT:   %11 = load i32, ptr %x8, align 4
+// CHECK-NEXT:   %cmp16 = icmp eq i32 %11, 2
+// CHECK-NEXT:   br i1 %cmp16, label %if.then17, label %if.end18
+// CHECK: if.then17:
+// CHECK-NEXT:   br label %expand.end20
+// CHECK: if.end18:
+// CHECK-NEXT:   %12 = load i32, ptr %y15, align 4
+// CHECK-NEXT:   %13 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add19 = add nsw i32 %13, %12
+// CHECK-NEXT:   store i32 %add19, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end20
+// CHECK: expand.end20:
+// CHECK-NEXT:   %14 = load i32, ptr %x8, align 4
+// CHECK-NEXT:   %15 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add21 = add nsw i32 %15, %14
+// CHECK-NEXT:   store i32 %add21, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT:   store i32 5, ptr %x23, align 4
+// CHECK-NEXT:   store i32 7, ptr %y24, align 4
+// CHECK-NEXT:   %16 = load i32, ptr %x23, align 4
+// CHECK-NEXT:   %cmp25 = icmp eq i32 %16, 6
+// CHECK-NEXT:   br i1 %cmp25, label %if.then26, label %if.end27
+// CHECK: if.then26:
+// CHECK-NEXT:   br label %expand.next29
+// CHECK: if.end27:
+// CHECK-NEXT:   %17 = load i32, ptr %y24, align 4
+// CHECK-NEXT:   %18 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add28 = add nsw i32 %18, %17
+// CHECK-NEXT:   store i32 %add28, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next29
+// CHECK: expand.next29:
+// CHECK-NEXT:   store i32 8, ptr %y30, align 4
+// CHECK-NEXT:   %19 = load i32, ptr %x23, align 4
+// CHECK-NEXT:   %cmp31 = icmp eq i32 %19, 6
+// CHECK-NEXT:   br i1 %cmp31, label %if.then32, label %if.end33
+// CHECK: if.then32:
+// CHECK-NEXT:   br label %expand.end35
+// CHECK: if.end33:
+// CHECK-NEXT:   %20 = load i32, ptr %y30, align 4
+// CHECK-NEXT:   %21 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add34 = add nsw i32 %21, %20
+// CHECK-NEXT:   store i32 %add34, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end35
+// CHECK: expand.end35:
+// CHECK-NEXT:   %22 = load i32, ptr %x23, align 4
+// CHECK-NEXT:   %23 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add36 = add nsw i32 %23, %22
+// CHECK-NEXT:   store i32 %add36, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next37
+// CHECK: expand.next37:
+// CHECK-NEXT:   store i32 6, ptr %x38, align 4
+// CHECK-NEXT:   store i32 7, ptr %y39, align 4
+// CHECK-NEXT:   %24 = load i32, ptr %x38, align 4
+// CHECK-NEXT:   %cmp40 = icmp eq i32 %24, 6
+// CHECK-NEXT:   br i1 %cmp40, label %if.then41, label %if.end42
+// CHECK: if.then41:
+// CHECK-NEXT:   br label %expand.next44
+// CHECK: if.end42:
+// CHECK-NEXT:   %25 = load i32, ptr %y39, align 4
+// CHECK-NEXT:   %26 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add43 = add nsw i32 %26, %25
+// CHECK-NEXT:   store i32 %add43, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next44
+// CHECK: expand.next44:
+// CHECK-NEXT:   store i32 8, ptr %y45, align 4
+// CHECK-NEXT:   %27 = load i32, ptr %x38, align 4
+// CHECK-NEXT:   %cmp46 = icmp eq i32 %27, 6
+// CHECK-NEXT:   br i1 %cmp46, label %if.then47, label %if.end48
+// CHECK: if.then47:
+// CHECK-NEXT:   br label %expand.end50
+// CHECK: if.end48:
+// CHECK-NEXT:   %28 = load i32, ptr %y45, align 4
+// CHECK-NEXT:   %29 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add49 = add nsw i32 %29, %28
+// CHECK-NEXT:   store i32 %add49, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end50
+// CHECK: expand.end50:
+// CHECK-NEXT:   %30 = load i32, ptr %x38, align 4
+// CHECK-NEXT:   %31 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add51 = add nsw i32 %31, %30
+// CHECK-NEXT:   store i32 %add51, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end52
+// CHECK: expand.end52:
+// CHECK-NEXT:   %32 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %32
+
+
+// CHECK-LABEL: define {{.*}} void @_Z5labelv()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %x7 = alloca i32, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %cmp = icmp eq i32 %0, 1
+// CHECK-NEXT:   br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT:   br label %a
+// CHECK: if.end:
+// CHECK-NEXT:   %1 = load i32, ptr %x, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} 1, i32 {{.*}} %1)
+// CHECK-NEXT:   br label %a
+// CHECK: a:
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 2, ptr %x1, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x1, align 4
+// CHECK-NEXT:   %cmp2 = icmp eq i32 %2, 1
+// CHECK-NEXT:   br i1 %cmp2, label %if.then3, label %if.end4
+// CHECK: if.then3:
+// CHECK-NEXT:   br label %a5
+// CHECK: if.end4:
+// CHECK-NEXT:   %3 = load i32, ptr %x1, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} 1, i32 {{.*}} %3)
+// CHECK-NEXT:   br label %a5
+// CHECK: a5:
+// CHECK-NEXT:   br label %expand.next6
+// CHECK: expand.next6:
+// CHECK-NEXT:   store i32 3, ptr %x7, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x7, align 4
+// CHECK-NEXT:   %cmp8 = icmp eq i32 %4, 1
+// CHECK-NEXT:   br i1 %cmp8, label %if.then9, label %if.end10
+// CHECK: if.then9:
+// CHECK-NEXT:   br label %a11
+// CHECK: if.end10:
+// CHECK-NEXT:   %5 = load i32, ptr %x7, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} 1, i32 {{.*}} %5)
+// CHECK-NEXT:   br label %a11
+// CHECK: a11:
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z12nested_labelv()
+// CHECK: entry:
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %y = alloca i32, align 4
+// CHECK-NEXT:   %y4 = alloca i32, align 4
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK-NEXT:   %y13 = alloca i32, align 4
+// CHECK-NEXT:   %y21 = alloca i32, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   store i32 3, ptr %y, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %y, align 4
+// CHECK-NEXT:   %cmp = icmp eq i32 %0, 3
+// CHECK-NEXT:   br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT:   br label %a
+// CHECK: if.end:
+// CHECK-NEXT:   %1 = load i32, ptr %y, align 4
+// CHECK-NEXT:   %cmp1 = icmp eq i32 %1, 4
+// CHECK-NEXT:   br i1 %cmp1, label %if.then2, label %if.end3
+// CHECK: if.then2:
+// CHECK-NEXT:   br label %end
+// CHECK: if.end3:
+// CHECK-NEXT:   %2 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %y, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} %2, i32 {{.*}} %3)
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 4, ptr %y4, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %y4, align 4
+// CHECK-NEXT:   %cmp5 = icmp eq i32 %4, 3
+// CHECK-NEXT:   br i1 %cmp5, label %if.then6, label %if.end7
+// CHECK: if.then6:
+// CHECK-NEXT:   br label %a
+// CHECK: if.end7:
+// CHECK-NEXT:   %5 = load i32, ptr %y4, align 4
+// CHECK-NEXT:   %cmp8 = icmp eq i32 %5, 4
+// CHECK-NEXT:   br i1 %cmp8, label %if.then9, label %if.end10
+// CHECK: if.then9:
+// CHECK-NEXT:   br label %end
+// CHECK: if.end10:
+// CHECK-NEXT:   %6 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %y4, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} %6, i32 {{.*}} %7)
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   br label %a
+// CHECK: a:
+// CHECK-NEXT:   br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT:   store i32 2, ptr %x12, align 4
+// CHECK-NEXT:   store i32 3, ptr %y13, align 4
+// CHECK-NEXT:   %8 = load i32, ptr %y13, align 4
+// CHECK-NEXT:   %cmp14 = icmp eq i32 %8, 3
+// CHECK-NEXT:   br i1 %cmp14, label %if.then15, label %if.end16
+// CHECK: if.then15:
+// CHECK-NEXT:   br label %a29
+// CHECK: if.end16:
+// CHECK-NEXT:   %9 = load i32, ptr %y13, align 4
+// CHECK-NEXT:   %cmp17 = icmp eq i32 %9, 4
+// CHECK-NEXT:   br i1 %cmp17, label %if.then18, label %if.end19
+// CHECK: if.then18:
+// CHECK-NEXT:   br label %end
+// CHECK: if.end19:
+// CHECK-NEXT:   %10 = load i32, ptr %x12, align 4
+// CHECK-NEXT:   %11 = load i32, ptr %y13, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} %10, i32 {{.*}} %11)
+// CHECK-NEXT:   br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT:   store i32 4, ptr %y21, align 4
+// CHECK-NEXT:   %12 = load i32, ptr %y21, align 4
+// CHECK-NEXT:   %cmp22 = icmp eq i32 %12, 3
+// CHECK-NEXT:   br i1 %cmp22, label %if.then23, label %if.end24
+// CHECK: if.then23:
+// CHECK-NEXT:   br label %a29
+// CHECK: if.end24:
+// CHECK-NEXT:   %13 = load i32, ptr %y21, align 4
+// CHECK-NEXT:   %cmp25 = icmp eq i32 %13, 4
+// CHECK-NEXT:   br i1 %cmp25, label %if.then26, label %if.end27
+// CHECK: if.then26:
+// CHECK-NEXT:   br label %end
+// CHECK: if.end27:
+// CHECK-NEXT:   %14 = load i32, ptr %x12, align 4
+// CHECK-NEXT:   %15 = load i32, ptr %y21, align 4
+// CHECK-NEXT:   call void @_Z1hii(i32 {{.*}} %14, i32 {{.*}} %15)
+// CHECK-NEXT:   br label %expand.end28
+// CHECK: expand.end28:
+// CHECK-NEXT:   br label %a29
+// CHECK: a29:
+// CHECK-NEXT:   br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT:   br label %end
+// CHECK: end:
+// CHECK-NEXT:   ret void
diff --git a/clang/test/CodeGenCXX/cxx2c-expansion-stmts-mangling.cpp b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-mangling.cpp
new file mode 100644
index 0000000000000..5616bc65c7853
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-mangling.cpp
@@ -0,0 +1,134 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: @_ZZ2f1vE1y = internal global i32 1, align 4
+// CHECK: @_ZZ2f1vE1y_0 = internal global i32 2, align 4
+// CHECK: @_ZZ2f1vE1y_1 = internal global i32 3, align 4
+// CHECK: @_ZZ2f1vE1y_2 = internal global i32 4, align 4
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f1v()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %x4 = alloca i32, align 4
+// CHECK-NEXT:   %x7 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr @_ZZ2f1vE1y, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %1, %0
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 2, ptr %x1, align 4
+// CHECK-NEXT:   %2 = load i32, ptr @_ZZ2f1vE1y_0, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add2 = add nsw i32 %3, %2
+// CHECK-NEXT:   store i32 %add2, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT:   store i32 3, ptr %x4, align 4
+// CHECK-NEXT:   %4 = load i32, ptr @_ZZ2f1vE1y_1, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add5 = add nsw i32 %5, %4
+// CHECK-NEXT:   store i32 %add5, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next6
+// CHECK: expand.next6:
+// CHECK-NEXT:   store i32 4, ptr %x7, align 4
+// CHECK-NEXT:   %6 = load i32, ptr @_ZZ2f1vE1y_2, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add8 = add nsw i32 %7, %6
+// CHECK-NEXT:   store i32 %add8, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   %8 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %8
+int f1() {
+  int sum = 0;
+  template for (constexpr auto x : {1, 2, 3, 4}) {
+    static int y = x;
+    sum += y;
+  }
+  return sum;
+}
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f2v()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %ref.tmp = alloca %class.anon, align 1
+// CHECK-NEXT:   %x1 = alloca i32, align 4
+// CHECK-NEXT:   %ref.tmp2 = alloca %class.anon.0, align 1
+// CHECK-NEXT:   %x6 = alloca i32, align 4
+// CHECK-NEXT:   %ref.tmp7 = alloca %class.anon.2, align 1
+// CHECK-NEXT:   %x11 = alloca i32, align 4
+// CHECK-NEXT:   %ref.tmp12 = alloca %class.anon.4, align 1
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %call = call {{.*}} i32 @_ZZ2f2vENKUlvE_clEv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   %0 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %0, %call
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store i32 2, ptr %x1, align 4
+// CHECK-NEXT:   %call3 = call {{.*}} i32 @_ZZ2f2vENKUlvE0_clEv(ptr {{.*}} %ref.tmp2)
+// CHECK-NEXT:   %1 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add4 = add nsw i32 %1, %call3
+// CHECK-NEXT:   store i32 %add4, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT:   store i32 3, ptr %x6, align 4
+// CHECK-NEXT:   %call8 = call {{.*}} i32 @_ZZ2f2vENKUlvE1_clEv(ptr {{.*}} %ref.tmp7)
+// CHECK-NEXT:   %2 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add9 = add nsw i32 %2, %call8
+// CHECK-NEXT:   store i32 %add9, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next10
+// CHECK: expand.next10:
+// CHECK-NEXT:   store i32 4, ptr %x11, align 4
+// CHECK-NEXT:   %call13 = call {{.*}} i32 @_ZZ2f2vENKUlvE2_clEv(ptr {{.*}} %ref.tmp12)
+// CHECK-NEXT:   %3 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add14 = add nsw i32 %3, %call13
+// CHECK-NEXT:   store i32 %add14, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %4
+int f2() {
+  int sum = 0;
+  template for (constexpr auto x : {1, 2, 3, 4}) {
+    sum += []{ return x; }();
+  }
+  return sum;
+}
+
+// CHECK-LABEL: define {{.*}} i32 @_ZZ2f2vENKUlvE_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   ret i32 1
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZZ2f2vENKUlvE0_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   ret i32 2
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZZ2f2vENKUlvE1_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   ret i32 3
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZZ2f2vENKUlvE2_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   ret i32 4
diff --git a/clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp
new file mode 100644
index 0000000000000..e0de7ced5baee
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp
@@ -0,0 +1,208 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+struct E {
+  int x, y;
+  constexpr E(int x, int y) : x{x}, y{y} {}
+};
+
+template <typename ...Es>
+int unexpanded_pack_good(Es ...es) {
+  int sum = 0;
+  ([&] {
+    template for (auto x : es) sum += x;
+    template for (Es e : {{5, 6}, {7, 8}}) sum += e.x + e.y;
+  }(), ...);
+  return sum;
+}
+
+int unexpanded_pack() {
+  return unexpanded_pack_good(E{1, 2}, E{3, 4});
+}
+
+
+// CHECK: %struct.E = type { i32, i32 }
+// CHECK: %class.anon = type { ptr, ptr }
+// CHECK: %class.anon.0 = type { ptr, ptr }
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z15unexpanded_packv()
+// CHECK: entry:
+// CHECK-NEXT:   %agg.tmp = alloca %struct.E, align 4
+// CHECK-NEXT:   %agg.tmp1 = alloca %struct.E, align 4
+// CHECK-NEXT:   call void @_ZN1EC1Eii(ptr {{.*}} %agg.tmp, i32 {{.*}} 1, i32 {{.*}} 2)
+// CHECK-NEXT:   call void @_ZN1EC1Eii(ptr {{.*}} %agg.tmp1, i32 {{.*}} 3, i32 {{.*}} 4)
+// CHECK-NEXT:   %0 = load i64, ptr %agg.tmp, align 4
+// CHECK-NEXT:   %1 = load i64, ptr %agg.tmp1, align 4
+// CHECK-NEXT:   %call = call {{.*}} i32 @_Z20unexpanded_pack_goodIJ1ES0_EEiDpT_(i64 %0, i64 %1)
+// CHECK-NEXT:   ret i32 %call
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z20unexpanded_pack_goodIJ1ES0_EEiDpT_(i64 %es.coerce, i64 %es.coerce2)
+// CHECK: entry:
+// CHECK-NEXT:   %es = alloca %struct.E, align 4
+// CHECK-NEXT:   %es3 = alloca %struct.E, align 4
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %ref.tmp = alloca %class.anon, align 8
+// CHECK-NEXT:   %ref.tmp4 = alloca %class.anon.0, align 8
+// CHECK-NEXT:   store i64 %es.coerce, ptr %es, align 4
+// CHECK-NEXT:   store i64 %es.coerce2, ptr %es3, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   %0 = getelementptr inbounds nuw %class.anon, ptr %ref.tmp, i32 0, i32 0
+// CHECK-NEXT:   store ptr %es, ptr %0, align 8
+// CHECK-NEXT:   %1 = getelementptr inbounds nuw %class.anon, ptr %ref.tmp, i32 0, i32 1
+// CHECK-NEXT:   store ptr %sum, ptr %1, align 8
+// CHECK-NEXT:   call void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE0_clEv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT:   %2 = getelementptr inbounds nuw %class.anon.0, ptr %ref.tmp4, i32 0, i32 0
+// CHECK-NEXT:   store ptr %es3, ptr %2, align 8
+// CHECK-NEXT:   %3 = getelementptr inbounds nuw %class.anon.0, ptr %ref.tmp4, i32 0, i32 1
+// CHECK-NEXT:   store ptr %sum, ptr %3, align 8
+// CHECK-NEXT:   call void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE_clEv(ptr {{.*}} %ref.tmp4)
+// CHECK-NEXT:   %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %4
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN1EC1Eii(ptr {{.*}} %this, i32 {{.*}} %x, i32 {{.*}} %y) {{.*}}
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %x.addr = alloca i32, align 4
+// CHECK-NEXT:   %y.addr = alloca i32, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   store i32 %x, ptr %x.addr, align 4
+// CHECK-NEXT:   store i32 %y, ptr %y.addr, align 4
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   %0 = load i32, ptr %x.addr, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %y.addr, align 4
+// CHECK-NEXT:   call void @_ZN1EC2Eii(ptr {{.*}} %this1, i32 {{.*}} %0, i32 {{.*}} %1)
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE0_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x3 = alloca i32, align 4
+// CHECK-NEXT:   %e = alloca %struct.E, align 4
+// CHECK-NEXT:   %e10 = alloca %struct.E, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   %1 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 0
+// CHECK-NEXT:   %2 = load ptr, ptr %1, align 8
+// CHECK-NEXT:   store ptr %2, ptr %0, align 8
+// CHECK-NEXT:   %3 = load ptr, ptr %0, align 8
+// CHECK-NEXT:   %x2 = getelementptr inbounds nuw %struct.E, ptr %3, i32 0, i32 0
+// CHECK-NEXT:   %4 = load i32, ptr %x2, align 4
+// CHECK-NEXT:   store i32 %4, ptr %x, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %6 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1
+// CHECK-NEXT:   %7 = load ptr, ptr %6, align 8
+// CHECK-NEXT:   %8 = load i32, ptr %7, align 4
+// CHECK-NEXT:   %add = add nsw i32 %8, %5
+// CHECK-NEXT:   store i32 %add, ptr %7, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %9 = load ptr, ptr %0, align 8
+// CHECK-NEXT:   %y = getelementptr inbounds nuw %struct.E, ptr %9, i32 0, i32 1
+// CHECK-NEXT:   %10 = load i32, ptr %y, align 4
+// CHECK-NEXT:   store i32 %10, ptr %x3, align 4
+// CHECK-NEXT:   %11 = load i32, ptr %x3, align 4
+// CHECK-NEXT:   %12 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1
+// CHECK-NEXT:   %13 = load ptr, ptr %12, align 8
+// CHECK-NEXT:   %14 = load i32, ptr %13, align 4
+// CHECK-NEXT:   %add4 = add nsw i32 %14, %11
+// CHECK-NEXT:   store i32 %add4, ptr %13, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   call void @_ZN1EC1Eii(ptr {{.*}} %e, i32 {{.*}} 5, i32 {{.*}} 6)
+// CHECK-NEXT:   %x5 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 0
+// CHECK-NEXT:   %15 = load i32, ptr %x5, align 4
+// CHECK-NEXT:   %y6 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 1
+// CHECK-NEXT:   %16 = load i32, ptr %y6, align 4
+// CHECK-NEXT:   %add7 = add nsw i32 %15, %16
+// CHECK-NEXT:   %17 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1
+// CHECK-NEXT:   %18 = load ptr, ptr %17, align 8
+// CHECK-NEXT:   %19 = load i32, ptr %18, align 4
+// CHECK-NEXT:   %add8 = add nsw i32 %19, %add7
+// CHECK-NEXT:   store i32 %add8, ptr %18, align 4
+// CHECK-NEXT:   br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT:   call void @_ZN1EC1Eii(ptr {{.*}} %e10, i32 {{.*}} 7, i32 {{.*}} 8)
+// CHECK-NEXT:   %x11 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 0
+// CHECK-NEXT:   %20 = load i32, ptr %x11, align 4
+// CHECK-NEXT:   %y12 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 1
+// CHECK-NEXT:   %21 = load i32, ptr %y12, align 4
+// CHECK-NEXT:   %add13 = add nsw i32 %20, %21
+// CHECK-NEXT:   %22 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1
+// CHECK-NEXT:   %23 = load ptr, ptr %22, align 8
+// CHECK-NEXT:   %24 = load i32, ptr %23, align 4
+// CHECK-NEXT:   %add14 = add nsw i32 %24, %add13
+// CHECK-NEXT:   store i32 %add14, ptr %23, align 4
+// CHECK-NEXT:   br label %expand.end15
+// CHECK: expand.end15:
+// CHECK-NEXT:   ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-NEXT:   %0 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %x3 = alloca i32, align 4
+// CHECK-NEXT:   %e = alloca %struct.E, align 4
+// CHECK-NEXT:   %e10 = alloca %struct.E, align 4
+// CHECK-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT:   %1 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 0
+// CHECK-NEXT:   %2 = load ptr, ptr %1, align 8
+// CHECK-NEXT:   store ptr %2, ptr %0, align 8
+// CHECK-NEXT:   %3 = load ptr, ptr %0, align 8
+// CHECK-NEXT:   %x2 = getelementptr inbounds nuw %struct.E, ptr %3, i32 0, i32 0
+// CHECK-NEXT:   %4 = load i32, ptr %x2, align 4
+// CHECK-NEXT:   store i32 %4, ptr %x, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %6 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1
+// CHECK-NEXT:   %7 = load ptr, ptr %6, align 8
+// CHECK-NEXT:   %8 = load i32, ptr %7, align 4
+// CHECK-NEXT:   %add = add nsw i32 %8, %5
+// CHECK-NEXT:   store i32 %add, ptr %7, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %9 = load ptr, ptr %0, align 8
+// CHECK-NEXT:   %y = getelementptr inbounds nuw %struct.E, ptr %9, i32 0, i32 1
+// CHECK-NEXT:   %10 = load i32, ptr %y, align 4
+// CHECK-NEXT:   store i32 %10, ptr %x3, align 4
+// CHECK-NEXT:   %11 = load i32, ptr %x3, align 4
+// CHECK-NEXT:   %12 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1
+// CHECK-NEXT:   %13 = load ptr, ptr %12, align 8
+// CHECK-NEXT:   %14 = load i32, ptr %13, align 4
+// CHECK-NEXT:   %add4 = add nsw i32 %14, %11
+// CHECK-NEXT:   store i32 %add4, ptr %13, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   call void @_ZN1EC1Eii(ptr {{.*}} %e, i32 {{.*}} 5, i32 {{.*}} 6)
+// CHECK-NEXT:   %x5 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 0
+// CHECK-NEXT:   %15 = load i32, ptr %x5, align 4
+// CHECK-NEXT:   %y6 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 1
+// CHECK-NEXT:   %16 = load i32, ptr %y6, align 4
+// CHECK-NEXT:   %add7 = add nsw i32 %15, %16
+// CHECK-NEXT:   %17 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1
+// CHECK-NEXT:   %18 = load ptr, ptr %17, align 8
+// CHECK-NEXT:   %19 = load i32, ptr %18, align 4
+// CHECK-NEXT:   %add8 = add nsw i32 %19, %add7
+// CHECK-NEXT:   store i32 %add8, ptr %18, align 4
+// CHECK-NEXT:   br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT:   call void @_ZN1EC1Eii(ptr {{.*}} %e10, i32 {{.*}} 7, i32 {{.*}} 8)
+// CHECK-NEXT:   %x11 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 0
+// CHECK-NEXT:   %20 = load i32, ptr %x11, align 4
+// CHECK-NEXT:   %y12 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 1
+// CHECK-NEXT:   %21 = load i32, ptr %y12, align 4
+// CHECK-NEXT:   %add13 = add nsw i32 %20, %21
+// CHECK-NEXT:   %22 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1
+// CHECK-NEXT:   %23 = load ptr, ptr %22, align 8
+// CHECK-NEXT:   %24 = load i32, ptr %23, align 4
+// CHECK-NEXT:   %add14 = add nsw i32 %24, %add13
+// CHECK-NEXT:   store i32 %add14, ptr %23, align 4
+// CHECK-NEXT:   br label %expand.end15
+// CHECK: expand.end15:
+// CHECK-NEXT:   ret void
diff --git a/clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp b/clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp
new file mode 100644
index 0000000000000..12c8328f41be0
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp
@@ -0,0 +1,551 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+// Iterating expansion statements are currently not supported.
+// XFAIL: *
+
+template <typename T, __SIZE_TYPE__ size>
+struct Array {
+  T data[size]{};
+  constexpr const T* begin() const { return data; }
+  constexpr const T* end() const { return data + size; }
+};
+
+int f1() {
+  static constexpr Array<int, 3> integers{1, 2, 3};
+  int sum = 0;
+  template for (auto x : integers) sum += x;
+  return sum;
+}
+
+int f2() {
+  static constexpr Array<int, 3> integers{1, 2, 3};
+  int sum = 0;
+  template for (constexpr auto x : integers) sum += x;
+  return sum;
+}
+
+int f3() {
+  static constexpr Array<int, 0> integers{};
+  int sum = 0;
+  template for (constexpr auto x : integers) {
+    static_assert(false, "not expanded");
+    sum += x;
+  }
+  return sum;
+}
+
+int f4() {
+  static constexpr Array<int, 2> a{1, 2};
+  static constexpr Array<int, 2> b{3, 4};
+  int sum = 0;
+
+  template for (auto x : a)
+    template for (auto y : b)
+      sum += x + y;
+
+  template for (constexpr auto x : a)
+    template for (constexpr auto y : b)
+      sum += x + y;
+
+  return sum;
+}
+
+struct Private {
+  static constexpr Array<int, 3> integers{1, 2, 3};
+  friend constexpr int friend_func();
+
+private:
+  constexpr const int* begin() const { return integers.begin(); }
+  constexpr const int* end() const { return integers.end(); }
+
+public:
+  static int member_func();
+};
+
+int Private::member_func() {
+  int sum = 0;
+  static constexpr Private p1;
+  template for (auto x : p1) sum += x;
+  return sum;
+}
+
+struct CustomIterator {
+  struct iterator {
+    int n;
+
+    constexpr iterator operator+(int m) const {
+      return {n + m};
+    }
+
+    constexpr void operator++() { ++n; }
+
+    constexpr int operator*() const {
+      return n;
+    }
+
+    friend constexpr bool operator!=(iterator a, iterator b) {
+      return a.n != b.n;
+    }
+
+    friend constexpr int operator-(iterator a, iterator b) {
+      return a.n - b.n;
+    }
+  };
+
+   constexpr iterator begin() const { return iterator(1); }
+   constexpr iterator end() const { return iterator(5); }
+};
+
+int custom_iterator() {
+  static constexpr CustomIterator c;
+  int sum = 0;
+  template for (auto x : c) sum += x;
+  template for (constexpr auto x : c) sum += x;
+  return sum;
+}
+
+// CHECK: @_ZZ2f1vE8integers = internal constant %struct.Array { [3 x i32] [i32 1, i32 2, i32 3] }, align 4
+// CHECK: @_ZZ2f2vE8integers = internal constant %struct.Array { [3 x i32] [i32 1, i32 2, i32 3] }, align 4
+// CHECK: @_ZZ2f3vE8integers = internal constant %struct.Array.0 zeroinitializer, align 4
+// CHECK: @_ZZ2f4vE1a = internal constant %struct.Array.1 { [2 x i32] [i32 1, i32 2] }, align 4
+// CHECK: @_ZZ2f4vE1b = internal constant %struct.Array.1 { [2 x i32] [i32 3, i32 4] }, align 4
+// CHECK: @_ZZN7Private11member_funcEvE2p1 = internal constant %struct.Private zeroinitializer, align 1
+// CHECK: @_ZZ15custom_iteratorvE1c = internal constant %struct.CustomIterator zeroinitializer, align 1
+// CHECK: @__const._Z15custom_iteratorv.__begin1 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 1 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__begin1.1 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 1 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__iter1 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 1 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__iter1.2 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 2 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__iter1.3 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 3 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__iter1.4 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 4 }, align 4
+// CHECK: @_ZN7Private8integersE = {{.*}} constant %struct.Array { [3 x i32] [i32 1, i32 2, i32 3] }, comdat, align 4
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f1v()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %__range1 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin1 = alloca ptr, align 8
+// CHECK-NEXT:   %__iter1 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %__iter11 = alloca ptr, align 8
+// CHECK-NEXT:   %x3 = alloca i32, align 4
+// CHECK-NEXT:   %__iter16 = alloca ptr, align 8
+// CHECK-NEXT:   %x8 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store ptr @_ZZ2f1vE8integers, ptr %__range1, align 8
+// CHECK-NEXT:   %call = call {{.*}} ptr @_ZNK5ArrayIiLm3EE5beginEv(ptr {{.*}} @_ZZ2f1vE8integers)
+// CHECK-NEXT:   store ptr %call, ptr %__begin1, align 8
+// CHECK-NEXT:   %0 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT:   %add.ptr = getelementptr inbounds i32, ptr %0, i64 0
+// CHECK-NEXT:   store ptr %add.ptr, ptr %__iter1, align 8
+// CHECK-NEXT:   %1 = load ptr, ptr %__iter1, align 8
+// CHECK-NEXT:   %2 = load i32, ptr %1, align 4
+// CHECK-NEXT:   store i32 %2, ptr %x, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %4, %3
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %5 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT:   %add.ptr2 = getelementptr inbounds i32, ptr %5, i64 1
+// CHECK-NEXT:   store ptr %add.ptr2, ptr %__iter11, align 8
+// CHECK-NEXT:   %6 = load ptr, ptr %__iter11, align 8
+// CHECK-NEXT:   %7 = load i32, ptr %6, align 4
+// CHECK-NEXT:   store i32 %7, ptr %x3, align 4
+// CHECK-NEXT:   %8 = load i32, ptr %x3, align 4
+// CHECK-NEXT:   %9 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add4 = add nsw i32 %9, %8
+// CHECK-NEXT:   store i32 %add4, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT:   %10 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT:   %add.ptr7 = getelementptr inbounds i32, ptr %10, i64 2
+// CHECK-NEXT:   store ptr %add.ptr7, ptr %__iter16, align 8
+// CHECK-NEXT:   %11 = load ptr, ptr %__iter16, align 8
+// CHECK-NEXT:   %12 = load i32, ptr %11, align 4
+// CHECK-NEXT:   store i32 %12, ptr %x8, align 4
+// CHECK-NEXT:   %13 = load i32, ptr %x8, align 4
+// CHECK-NEXT:   %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add9 = add nsw i32 %14, %13
+// CHECK-NEXT:   store i32 %add9, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   %15 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %15
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f2v()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %__range1 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin1 = alloca ptr, align 8
+// CHECK-NEXT:   %__iter1 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %__iter11 = alloca ptr, align 8
+// CHECK-NEXT:   %x2 = alloca i32, align 4
+// CHECK-NEXT:   %__iter15 = alloca ptr, align 8
+// CHECK-NEXT:   %x6 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store ptr @_ZZ2f2vE8integers, ptr %__range1, align 8
+// CHECK-NEXT:   store ptr @_ZZ2f2vE8integers, ptr %__begin1, align 8
+// CHECK-NEXT:   store ptr @_ZZ2f2vE8integers, ptr %__iter1, align 8
+// CHECK-NEXT:   store i32 1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %0, 1
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   store ptr getelementptr (i8, ptr @_ZZ2f2vE8integers, i64 4), ptr %__iter11, align 8
+// CHECK-NEXT:   store i32 2, ptr %x2, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add3 = add nsw i32 %1, 2
+// CHECK-NEXT:   store i32 %add3, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT:   store ptr getelementptr (i8, ptr @_ZZ2f2vE8integers, i64 8), ptr %__iter15, align 8
+// CHECK-NEXT:   store i32 3, ptr %x6, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add7 = add nsw i32 %2, 3
+// CHECK-NEXT:   store i32 %add7, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   %3 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %3
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f3v()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %__range1 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin1 = alloca ptr, align 8
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store ptr @_ZZ2f3vE8integers, ptr %__range1, align 8
+// CHECK-NEXT:   store ptr @_ZZ2f3vE8integers, ptr %__begin1, align 8
+// CHECK-NEXT:   %0 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %0
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f4v()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %__range1 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin1 = alloca ptr, align 8
+// CHECK-NEXT:   %__iter1 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %__range2 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin2 = alloca ptr, align 8
+// CHECK-NEXT:   %__iter2 = alloca ptr, align 8
+// CHECK-NEXT:   %y = alloca i32, align 4
+// CHECK-NEXT:   %__iter24 = alloca ptr, align 8
+// CHECK-NEXT:   %y6 = alloca i32, align 4
+// CHECK-NEXT:   %__iter110 = alloca ptr, align 8
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK-NEXT:   %__range213 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin214 = alloca ptr, align 8
+// CHECK-NEXT:   %__iter216 = alloca ptr, align 8
+// CHECK-NEXT:   %y18 = alloca i32, align 4
+// CHECK-NEXT:   %__iter222 = alloca ptr, align 8
+// CHECK-NEXT:   %y24 = alloca i32, align 4
+// CHECK-NEXT:   %__range129 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin130 = alloca ptr, align 8
+// CHECK-NEXT:   %__iter131 = alloca ptr, align 8
+// CHECK-NEXT:   %x32 = alloca i32, align 4
+// CHECK-NEXT:   %__range233 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin234 = alloca ptr, align 8
+// CHECK-NEXT:   %__iter235 = alloca ptr, align 8
+// CHECK-NEXT:   %y36 = alloca i32, align 4
+// CHECK-NEXT:   %__iter239 = alloca ptr, align 8
+// CHECK-NEXT:   %y40 = alloca i32, align 4
+// CHECK-NEXT:   %__iter144 = alloca ptr, align 8
+// CHECK-NEXT:   %x45 = alloca i32, align 4
+// CHECK-NEXT:   %__range246 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin247 = alloca ptr, align 8
+// CHECK-NEXT:   %__iter248 = alloca ptr, align 8
+// CHECK-NEXT:   %y49 = alloca i32, align 4
+// CHECK-NEXT:   %__iter252 = alloca ptr, align 8
+// CHECK-NEXT:   %y53 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1a, ptr %__range1, align 8
+// CHECK-NEXT:   %call = call {{.*}} ptr @_ZNK5ArrayIiLm2EE5beginEv(ptr {{.*}} @_ZZ2f4vE1a)
+// CHECK-NEXT:   store ptr %call, ptr %__begin1, align 8
+// CHECK-NEXT:   %0 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT:   %add.ptr = getelementptr inbounds i32, ptr %0, i64 0
+// CHECK-NEXT:   store ptr %add.ptr, ptr %__iter1, align 8
+// CHECK-NEXT:   %1 = load ptr, ptr %__iter1, align 8
+// CHECK-NEXT:   %2 = load i32, ptr %1, align 4
+// CHECK-NEXT:   store i32 %2, ptr %x, align 4
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1b, ptr %__range2, align 8
+// CHECK-NEXT:   %call1 = call {{.*}} ptr @_ZNK5ArrayIiLm2EE5beginEv(ptr {{.*}} @_ZZ2f4vE1b)
+// CHECK-NEXT:   store ptr %call1, ptr %__begin2, align 8
+// CHECK-NEXT:   %3 = load ptr, ptr %__begin2, align 8
+// CHECK-NEXT:   %add.ptr2 = getelementptr inbounds i32, ptr %3, i64 0
+// CHECK-NEXT:   store ptr %add.ptr2, ptr %__iter2, align 8
+// CHECK-NEXT:   %4 = load ptr, ptr %__iter2, align 8
+// CHECK-NEXT:   %5 = load i32, ptr %4, align 4
+// CHECK-NEXT:   store i32 %5, ptr %y, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %y, align 4
+// CHECK-NEXT:   %add = add nsw i32 %6, %7
+// CHECK-NEXT:   %8 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add3 = add nsw i32 %8, %add
+// CHECK-NEXT:   store i32 %add3, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %9 = load ptr, ptr %__begin2, align 8
+// CHECK-NEXT:   %add.ptr5 = getelementptr inbounds i32, ptr %9, i64 1
+// CHECK-NEXT:   store ptr %add.ptr5, ptr %__iter24, align 8
+// CHECK-NEXT:   %10 = load ptr, ptr %__iter24, align 8
+// CHECK-NEXT:   %11 = load i32, ptr %10, align 4
+// CHECK-NEXT:   store i32 %11, ptr %y6, align 4
+// CHECK-NEXT:   %12 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %13 = load i32, ptr %y6, align 4
+// CHECK-NEXT:   %add7 = add nsw i32 %12, %13
+// CHECK-NEXT:   %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add8 = add nsw i32 %14, %add7
+// CHECK-NEXT:   store i32 %add8, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT:   %15 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT:   %add.ptr11 = getelementptr inbounds i32, ptr %15, i64 1
+// CHECK-NEXT:   store ptr %add.ptr11, ptr %__iter110, align 8
+// CHECK-NEXT:   %16 = load ptr, ptr %__iter110, align 8
+// CHECK-NEXT:   %17 = load i32, ptr %16, align 4
+// CHECK-NEXT:   store i32 %17, ptr %x12, align 4
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1b, ptr %__range213, align 8
+// CHECK-NEXT:   %call15 = call {{.*}} ptr @_ZNK5ArrayIiLm2EE5beginEv(ptr {{.*}} @_ZZ2f4vE1b)
+// CHECK-NEXT:   store ptr %call15, ptr %__begin214, align 8
+// CHECK-NEXT:   %18 = load ptr, ptr %__begin214, align 8
+// CHECK-NEXT:   %add.ptr17 = getelementptr inbounds i32, ptr %18, i64 0
+// CHECK-NEXT:   store ptr %add.ptr17, ptr %__iter216, align 8
+// CHECK-NEXT:   %19 = load ptr, ptr %__iter216, align 8
+// CHECK-NEXT:   %20 = load i32, ptr %19, align 4
+// CHECK-NEXT:   store i32 %20, ptr %y18, align 4
+// CHECK-NEXT:   %21 = load i32, ptr %x12, align 4
+// CHECK-NEXT:   %22 = load i32, ptr %y18, align 4
+// CHECK-NEXT:   %add19 = add nsw i32 %21, %22
+// CHECK-NEXT:   %23 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add20 = add nsw i32 %23, %add19
+// CHECK-NEXT:   store i32 %add20, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next21
+// CHECK: expand.next21:
+// CHECK-NEXT:   %24 = load ptr, ptr %__begin214, align 8
+// CHECK-NEXT:   %add.ptr23 = getelementptr inbounds i32, ptr %24, i64 1
+// CHECK-NEXT:   store ptr %add.ptr23, ptr %__iter222, align 8
+// CHECK-NEXT:   %25 = load ptr, ptr %__iter222, align 8
+// CHECK-NEXT:   %26 = load i32, ptr %25, align 4
+// CHECK-NEXT:   store i32 %26, ptr %y24, align 4
+// CHECK-NEXT:   %27 = load i32, ptr %x12, align 4
+// CHECK-NEXT:   %28 = load i32, ptr %y24, align 4
+// CHECK-NEXT:   %add25 = add nsw i32 %27, %28
+// CHECK-NEXT:   %29 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add26 = add nsw i32 %29, %add25
+// CHECK-NEXT:   store i32 %add26, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end27
+// CHECK: expand.end27:
+// CHECK-NEXT:   br label %expand.end28
+// CHECK: expand.end28:
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1a, ptr %__range129, align 8
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1a, ptr %__begin130, align 8
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1a, ptr %__iter131, align 8
+// CHECK-NEXT:   store i32 1, ptr %x32, align 4
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1b, ptr %__range233, align 8
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1b, ptr %__begin234, align 8
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1b, ptr %__iter235, align 8
+// CHECK-NEXT:   store i32 3, ptr %y36, align 4
+// CHECK-NEXT:   %30 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add37 = add nsw i32 %30, 4
+// CHECK-NEXT:   store i32 %add37, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next38
+// CHECK: expand.next38:
+// CHECK-NEXT:   store ptr getelementptr (i8, ptr @_ZZ2f4vE1b, i64 4), ptr %__iter239, align 8
+// CHECK-NEXT:   store i32 4, ptr %y40, align 4
+// CHECK-NEXT:   %31 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add41 = add nsw i32 %31, 5
+// CHECK-NEXT:   store i32 %add41, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end42
+// CHECK: expand.end42:
+// CHECK-NEXT:   br label %expand.next43
+// CHECK: expand.next43:
+// CHECK-NEXT:   store ptr getelementptr (i8, ptr @_ZZ2f4vE1a, i64 4), ptr %__iter144, align 8
+// CHECK-NEXT:   store i32 2, ptr %x45, align 4
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1b, ptr %__range246, align 8
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1b, ptr %__begin247, align 8
+// CHECK-NEXT:   store ptr @_ZZ2f4vE1b, ptr %__iter248, align 8
+// CHECK-NEXT:   store i32 3, ptr %y49, align 4
+// CHECK-NEXT:   %32 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add50 = add nsw i32 %32, 5
+// CHECK-NEXT:   store i32 %add50, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next51
+// CHECK: expand.next51:
+// CHECK-NEXT:   store ptr getelementptr (i8, ptr @_ZZ2f4vE1b, i64 4), ptr %__iter252, align 8
+// CHECK-NEXT:   store i32 4, ptr %y53, align 4
+// CHECK-NEXT:   %33 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add54 = add nsw i32 %33, 6
+// CHECK-NEXT:   store i32 %add54, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end55
+// CHECK: expand.end55:
+// CHECK-NEXT:   br label %expand.end56
+// CHECK: expand.end56:
+// CHECK-NEXT:   %34 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %34
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN7Private11member_funcEv()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %__range1 = alloca ptr, align 8
+// CHECK-NEXT:   %__begin1 = alloca ptr, align 8
+// CHECK-NEXT:   %__iter1 = alloca ptr, align 8
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK-NEXT:   %__iter11 = alloca ptr, align 8
+// CHECK-NEXT:   %x3 = alloca i32, align 4
+// CHECK-NEXT:   %__iter16 = alloca ptr, align 8
+// CHECK-NEXT:   %x8 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store ptr @_ZZN7Private11member_funcEvE2p1, ptr %__range1, align 8
+// CHECK-NEXT:   %call = call {{.*}} ptr @_ZNK7Private5beginEv(ptr {{.*}} @_ZZN7Private11member_funcEvE2p1)
+// CHECK-NEXT:   store ptr %call, ptr %__begin1, align 8
+// CHECK-NEXT:   %0 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT:   %add.ptr = getelementptr inbounds i32, ptr %0, i64 0
+// CHECK-NEXT:   store ptr %add.ptr, ptr %__iter1, align 8
+// CHECK-NEXT:   %1 = load ptr, ptr %__iter1, align 8
+// CHECK-NEXT:   %2 = load i32, ptr %1, align 4
+// CHECK-NEXT:   store i32 %2, ptr %x, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %4, %3
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %5 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT:   %add.ptr2 = getelementptr inbounds i32, ptr %5, i64 1
+// CHECK-NEXT:   store ptr %add.ptr2, ptr %__iter11, align 8
+// CHECK-NEXT:   %6 = load ptr, ptr %__iter11, align 8
+// CHECK-NEXT:   %7 = load i32, ptr %6, align 4
+// CHECK-NEXT:   store i32 %7, ptr %x3, align 4
+// CHECK-NEXT:   %8 = load i32, ptr %x3, align 4
+// CHECK-NEXT:   %9 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add4 = add nsw i32 %9, %8
+// CHECK-NEXT:   store i32 %add4, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT:   %10 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT:   %add.ptr7 = getelementptr inbounds i32, ptr %10, i64 2
+// CHECK-NEXT:   store ptr %add.ptr7, ptr %__iter16, align 8
+// CHECK-NEXT:   %11 = load ptr, ptr %__iter16, align 8
+// CHECK-NEXT:   %12 = load i32, ptr %11, align 4
+// CHECK-NEXT:   store i32 %12, ptr %x8, align 4
+// CHECK-NEXT:   %13 = load i32, ptr %x8, align 4
+// CHECK-NEXT:   %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add9 = add nsw i32 %14, %13
+// CHECK-NEXT:   store i32 %add9, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   %15 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %15
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z15custom_iteratorv()
+// CHECK: entry:
+// CHECK-NEXT:   %sum = alloca i32, align 4
+// CHECK-NEXT:   %__range1 = alloca ptr, align 8
+// CHECK: %__begin1 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK: %__iter1 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT:   %x = alloca i32, align 4
+// CHECK: %__iter12 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT:   %x5 = alloca i32, align 4
+// CHECK: %__iter19 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT:   %x12 = alloca i32, align 4
+// CHECK: %__iter116 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT:   %x19 = alloca i32, align 4
+// CHECK-NEXT:   %__range122 = alloca ptr, align 8
+// CHECK: %__begin123 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK: %__iter124 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT:   %x25 = alloca i32, align 4
+// CHECK: %__iter128 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT:   %x29 = alloca i32, align 4
+// CHECK: %__iter132 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT:   %x33 = alloca i32, align 4
+// CHECK: %__iter136 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT:   %x37 = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %sum, align 4
+// CHECK-NEXT:   store ptr @_ZZ15custom_iteratorvE1c, ptr %__range1, align 8
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__begin1, ptr align 4 @__const._Z15custom_iteratorv.__begin1, i64 4, i1 false)
+// CHECK-NEXT:   %call = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 0)
+// CHECK: %coerce.dive = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %__iter1, i32 0, i32 0
+// CHECK-NEXT:   store i32 %call, ptr %coerce.dive, align 4
+// CHECK-NEXT:   %call1 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %__iter1)
+// CHECK-NEXT:   store i32 %call1, ptr %x, align 4
+// CHECK-NEXT:   %0 = load i32, ptr %x, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add = add nsw i32 %1, %0
+// CHECK-NEXT:   store i32 %add, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT:   %call3 = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 1)
+// CHECK: %coerce.dive4 = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %__iter12, i32 0, i32 0
+// CHECK-NEXT:   store i32 %call3, ptr %coerce.dive4, align 4
+// CHECK-NEXT:   %call6 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %__iter12)
+// CHECK-NEXT:   store i32 %call6, ptr %x5, align 4
+// CHECK-NEXT:   %2 = load i32, ptr %x5, align 4
+// CHECK-NEXT:   %3 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add7 = add nsw i32 %3, %2
+// CHECK-NEXT:   store i32 %add7, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT:   %call10 = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 2)
+// CHECK: %coerce.dive11 = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %__iter19, i32 0, i32 0
+// CHECK-NEXT:   store i32 %call10, ptr %coerce.dive11, align 4
+// CHECK-NEXT:   %call13 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %__iter19)
+// CHECK-NEXT:   store i32 %call13, ptr %x12, align 4
+// CHECK-NEXT:   %4 = load i32, ptr %x12, align 4
+// CHECK-NEXT:   %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add14 = add nsw i32 %5, %4
+// CHECK-NEXT:   store i32 %add14, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next15
+// CHECK: expand.next15:
+// CHECK-NEXT:   %call17 = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 3)
+// CHECK: %coerce.dive18 = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %__iter116, i32 0, i32 0
+// CHECK-NEXT:   store i32 %call17, ptr %coerce.dive18, align 4
+// CHECK-NEXT:   %call20 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %__iter116)
+// CHECK-NEXT:   store i32 %call20, ptr %x19, align 4
+// CHECK-NEXT:   %6 = load i32, ptr %x19, align 4
+// CHECK-NEXT:   %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add21 = add nsw i32 %7, %6
+// CHECK-NEXT:   store i32 %add21, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT:   store ptr @_ZZ15custom_iteratorvE1c, ptr %__range122, align 8
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__begin123, ptr align 4 @__const._Z15custom_iteratorv.__begin1.1, i64 4, i1 false)
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__iter124, ptr align 4 @__const._Z15custom_iteratorv.__iter1, i64 4, i1 false)
+// CHECK-NEXT:   store i32 1, ptr %x25, align 4
+// CHECK-NEXT:   %8 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add26 = add nsw i32 %8, 1
+// CHECK-NEXT:   store i32 %add26, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next27
+// CHECK: expand.next27:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__iter128, ptr align 4 @__const._Z15custom_iteratorv.__iter1.2, i64 4, i1 false)
+// CHECK-NEXT:   store i32 2, ptr %x29, align 4
+// CHECK-NEXT:   %9 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add30 = add nsw i32 %9, 2
+// CHECK-NEXT:   store i32 %add30, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next31
+// CHECK: expand.next31:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__iter132, ptr align 4 @__const._Z15custom_iteratorv.__iter1.3, i64 4, i1 false)
+// CHECK-NEXT:   store i32 3, ptr %x33, align 4
+// CHECK-NEXT:   %10 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add34 = add nsw i32 %10, 3
+// CHECK-NEXT:   store i32 %add34, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.next35
+// CHECK: expand.next35:
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__iter136, ptr align 4 @__const._Z15custom_iteratorv.__iter1.4, i64 4, i1 false)
+// CHECK-NEXT:   store i32 4, ptr %x37, align 4
+// CHECK-NEXT:   %11 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   %add38 = add nsw i32 %11, 4
+// CHECK-NEXT:   store i32 %add38, ptr %sum, align 4
+// CHECK-NEXT:   br label %expand.end39
+// CHECK: expand.end39:
+// CHECK-NEXT:   %12 = load i32, ptr %sum, align 4
+// CHECK-NEXT:   ret i32 %12
diff --git a/clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp b/clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp
new file mode 100644
index 0000000000000..0c0a229abeedc
--- /dev/null
+++ b/clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 %s -std=c++23 -fsyntax-only -verify
+
+void f() {
+  template for (char x : "123") {} // expected-error {{expansion statements are only supported in C++2c}}
+}
diff --git a/clang/test/Parser/cxx2c-expansion-statements.cpp b/clang/test/Parser/cxx2c-expansion-statements.cpp
new file mode 100644
index 0000000000000..736f9fead383c
--- /dev/null
+++ b/clang/test/Parser/cxx2c-expansion-statements.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
+namespace std {
+template <typename T>
+struct initializer_list {
+  const T* a;
+  const T* b;
+  initializer_list(T*, T*) {}
+};
+}
+
+void bad() {
+  template for; // expected-error {{expected '(' after 'for'}}
+  template for (); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
+  template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
+  template for (;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
+  template for (int x;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
+  template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}}
+  template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
+  template for (auto y : {1})]; // expected-error {{expected expression}}
+  template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}}
+  template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+  template for (register auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'register'}} expected-error {{ISO C++17 does not allow 'register' storage class specifier}}
+  template for (__private_extern__ auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+  template for (extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
+  template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+  template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
+  template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
+  template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
+  template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+  template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
+  template for (consteval auto y : {1, 2});  // expected-error {{consteval can only be used in function declarations}}
+  template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+  template for (int x; extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
+  template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+  template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
+  template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
+  template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
+  template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+  template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
+  template for (int x; consteval auto y : {1, 2});  // expected-error {{consteval can only be used in function declarations}}
+  template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}}
+  template for (3 : "error") // expected-error {{expansion statement declaration must declare a variable}} \
+                                expected-error {{expansion statement must use the syntax of a range-based for loop}}
+    ;
+  template while (true) {} // expected-error {{expected '<' after 'template'}}
+  ; // Semicolon for synchronisation; otherwise, the parser skips over next statement...
+  template do {} while (true); // expected-error {{expected '<' after 'template'}}
+  for template (int x : {}) {} // expected-error {{'for template' is invalid; use 'template for' instead}}
+}
+
+void good() {
+  template for (auto y : {});
+  template for (auto y : {1, 2});
+  template for (int x; auto y : {1, 2});
+  template for (int x; int y : {1, 2});
+  template for (int x; constexpr auto y : {1, 2});
+  template for (int x; constexpr int y : {1, 2});
+  template for (constexpr int a : {1, 2}) {
+    template for (constexpr int b : {1, 2}) {
+      template for (constexpr int c : {1, 2});
+    }
+  }
+}
+
+void trailing_comma() {
+  template for (int x : {1, 2,}) {}
+  template for (int x : {,}) {} // expected-error {{expected expression}}
+}
diff --git a/clang/test/SemaCXX/cxx2c-expansion-statements-shadow.cpp b/clang/test/SemaCXX/cxx2c-expansion-statements-shadow.cpp
new file mode 100644
index 0000000000000..c82fd0b5e39be
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-expansion-statements-shadow.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -Wshadow -verify
+// expected-no-diagnostics
+
+// Test that -Wshadow doesn't fire on implicit variable declarations
+// introduced in the expansion of an expansion statement.
+
+void f() {
+  int a[4];
+  template for (int __u0; auto x : a) {}
+  template for (auto x : a) { int __u0; }
+}
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts-control-flow.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts-control-flow.cpp
new file mode 100644
index 0000000000000..51b383d63cdcb
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts-control-flow.cpp
@@ -0,0 +1,135 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fblocks -verify
+
+void g(int);
+
+void label() {
+  template for (auto x : {1, 2}) {
+    invalid1:; // expected-error {{labels are not allowed in expansion statements}}
+    invalid2:; // expected-error {{labels are not allowed in expansion statements}}
+    goto invalid1; // expected-error {{use of undeclared label 'invalid1'}}
+  }
+
+  template for (auto x : {1, 2}) {
+    (void) [] {
+      template for (auto x : {1, 2}) {
+        invalid3:; // expected-error {{labels are not allowed in expansion statements}}
+      }
+      ok:;
+    };
+
+    (void) ^{
+      template for (auto x : {1, 2}) {
+        invalid4:; // expected-error {{labels are not allowed in expansion statements}}
+      }
+      ok:;
+    };
+
+    struct X {
+      void f() {
+        ok:;
+      }
+    };
+  }
+
+  // GNU local labels are allowed.
+  template for (auto x : {1, 2}) {
+    __label__ a;
+    if (x == 1) goto a;
+    a:;
+    if (x == 1) goto a;
+  }
+
+  // Likewise, jumping *out* of an expansion statement is fine.
+  template for (auto x : {1, 2}) {
+    if (x == 1) goto lbl;
+    g(x);
+  }
+  lbl:;
+  template for (auto x : {1, 2}) {
+    if (x == 1) goto lbl;
+    g(x);
+  }
+
+  // Jumping into one is not possible, as local labels aren't visible
+  // outside the block that declares them, and non-local labels are invalid.
+  goto exp1; // expected-error {{use of undeclared label 'exp1'}}
+  goto exp3; // expected-error {{use of undeclared label 'exp3'}}
+  template for (auto x : {1, 2}) {
+    __label__ exp1, exp2;
+    exp1:;
+    exp2:;
+    exp3:; // expected-error {{labels are not allowed in expansion statements}}
+  }
+  goto exp2; // expected-error {{use of undeclared label 'exp2'}}
+
+  // Allow jumping from inside an expansion statement to a local label in
+  // one of its parents.
+  out1:;
+  template for (auto x : {1, 2}) {
+    __label__ x, y;
+    x:
+    goto out1;
+    goto out2;
+    template for (auto x : {3, 4}) {
+      goto x;
+      goto y;
+      goto out1;
+      goto out2;
+    }
+    y:
+  }
+  out2:;
+}
+
+
+void case_default(int i) {
+  switch (i) { // expected-note 3 {{switch statement is here}}
+    template for (auto x : {1, 2}) {
+      case 1:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
+        template for (auto x : {1, 2}) {
+          case 2:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
+        }
+      default: // expected-error {{'default' belongs to 'switch' outside enclosing expansion statement}}
+        switch (i) {  // expected-note {{switch statement is here}}
+          case 3:;
+          default:
+            template for (auto x : {1, 2}) {
+              case 4:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
+            }
+        }
+    }
+  }
+
+  template for (auto x : {1, 2}) {
+    switch (i) {
+      case 1:;
+      default:
+    }
+  }
+
+  // Ensure that we diagnose this even if the statements would be discarded.
+  switch (i) { // expected-note 2 {{switch statement is here}}
+    template for (auto x : {}) {
+      case 1:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
+      default:; // expected-error {{'default' belongs to 'switch' outside enclosing expansion statement}}
+    }
+  }
+}
+
+void case_constexpr(int i) {
+  template for (constexpr auto x : {1, 2, 3}) { // expected-note {{in instantiation of expansion statement requested here}}
+    switch (i) {
+      case x:; // expected-note {{previous case defined here}}
+      case 2:; // expected-error {{duplicate case value: 'x' and '2' both equal '2'}}
+      default:;
+    }
+  }
+
+  template for (constexpr auto x : {1, 2, 3}) {
+    switch (i) {
+      case x:;
+      case 4:;
+      default:;
+    }
+  }
+}
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
new file mode 100644
index 0000000000000..f98ddd0fa949d
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
@@ -0,0 +1,1590 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks -Wno-vla-cxx-extension -fconstexpr-steps=10000 -verify=expected,old-interp
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks -Wno-vla-cxx-extension -fconstexpr-steps=10000 -verify=expected,new-interp -fexperimental-new-constant-interpreter
+namespace std {
+template <typename T>
+struct initializer_list {
+  const T* a;
+  const T* b;
+  initializer_list(T* a, T* b): a{a}, b{b} {}
+};
+}
+
+struct S {
+  int x;
+  constexpr S(int x) : x{x} {}
+};
+
+void g(int); // #g
+template <int n> constexpr int tg() { return n; }
+
+void f1() {
+  template for (auto x : {}) static_assert(false, "discarded");
+  template for (constexpr auto x : {}) static_assert(false, "discarded");
+  template for (auto x : {1}) g(x);
+  template for (auto x : {1, 2, 3}) g(x);
+  template for (constexpr auto x : {1}) g(x);
+  template for (constexpr auto x : {1, 2, 3}) g(x);
+  template for (constexpr auto x : {1}) tg<x>();
+  template for (constexpr auto x : {1, 2, 3})
+    static_assert(tg<x>());
+
+  template for (int x : {1, 2, 3}) g(x);
+  template for (S x : {1, 2, 3}) g(x.x);
+  template for (constexpr S x : {1, 2, 3}) tg<x.x>();
+
+  template for (int x : {"1", S(1), {1, 2}}) { // expected-error {{cannot initialize a variable of type 'int' with an lvalue of type 'const char[2]'}} \
+                                                  expected-error {{no viable conversion from 'S' to 'int'}} \
+                                                  expected-error {{excess elements in scalar initializer}} \
+                                                  expected-note 3 {{in instantiation of expansion statement requested here}}
+    g(x);
+  }
+
+  template for (constexpr auto x : {1, 2, 3, 4}) { // expected-note 3 {{in instantiation of expansion statement requested here}}
+    static_assert(tg<x>() == 4); // expected-error 3 {{static assertion failed due to requirement 'tg<x>() == 4'}} \
+                                    expected-note {{expression evaluates to '1 == 4'}} \
+                                    expected-note {{expression evaluates to '2 == 4'}} \
+                                    expected-note {{expression evaluates to '3 == 4'}}
+  }
+
+
+  template for (constexpr auto x : {1, 2}) { // expected-note 2 {{in instantiation of expansion statement requested here}}
+    static_assert(false, "not discarded"); // expected-error 2 {{static assertion failed: not discarded}}
+  }
+}
+
+template <typename T>
+void t1() {
+  template for (T x : {}) g(x);
+  template for (constexpr T x : {}) g(x);
+  template for (auto x : {}) g(x);
+  template for (constexpr auto x : {}) g(x);
+  template for (T x : {1, 2}) g(x);
+  template for (T x : {T(1), T(2)}) g(x);
+  template for (auto x : {T(1), T(2)}) g(x);
+  template for (constexpr T x : {T(1), T(2)}) static_assert(tg<x>());
+  template for (constexpr auto x : {T(1), T(2)}) static_assert(tg<x>());
+}
+
+template <typename U>
+struct s1 {
+  template <typename T>
+  void tf() {
+      template for (T x : {}) g(x);
+      template for (constexpr T x : {}) g(x);
+      template for (U x : {}) g(x);
+      template for (constexpr U x : {}) g(x);
+      template for (auto x : {}) g(x);
+      template for (constexpr auto x : {}) g(x);
+      template for (T x : {1, 2}) g(x);
+      template for (U x : {1, 2}) g(x);
+      template for (U x : {T(1), T(2)}) g(x);
+      template for (T x : {U(1), U(2)}) g(x);
+      template for (auto x : {T(1), T(2)}) g(x);
+      template for (auto x : {U(1), T(2)}) g(x);
+      template for (constexpr U x : {T(1), T(2)}) static_assert(tg<x>());
+      template for (constexpr T x : {U(1), U(2)}) static_assert(tg<x>());
+      template for (constexpr auto x : {T(1), U(2)}) static_assert(tg<x>());
+    }
+};
+
+template <typename T>
+void t2() {
+  template for (T x : {}) g(x);
+}
+
+void f2() {
+  t1<int>();
+  t1<long>();
+  s1<long>().tf<long>();
+  s1<int>().tf<int>();
+  s1<int>().tf<long>();
+  s1<long>().tf<int>();
+  t2<S>();
+  t2<S[1231]>();
+  t2<S***>();
+}
+
+template <__SIZE_TYPE__ size>
+struct String {
+  char data[size];
+
+  template <__SIZE_TYPE__ n>
+  constexpr String(const char (&str)[n]) { __builtin_memcpy(data, str, n); }
+
+  constexpr const char* begin() const { return data; }
+  constexpr const char* end() const { return data + size - 1; }
+};
+
+template <__SIZE_TYPE__ n>
+String(const char (&str)[n]) -> String<n>;
+
+// Note: Remove this test once we do support them.
+int iterating_expansion_stmts_unsupported() {
+  static constexpr String s{"abcd"};
+  int count = 0;
+  template for (constexpr auto x : s) count++; // expected-error {{iterating expansion statements are not yet supported}}
+  return count;
+}
+
+template <typename T>
+int iterating_expansion_stmts_unsupported_dependent() {
+  static constexpr String s{"abcd"};
+  int count = 0;
+  template for (auto x : T(s)) count++; // expected-error {{iterating expansion statements are not yet supported}}
+  return count;
+}
+
+void iterating_expansion_stmts_unsupported_dependent_instantiate() {
+  iterating_expansion_stmts_unsupported_dependent<String<5>>(); // expected-note {{in instantiation of}}
+}
+
+#if 0 // Disabled until we support iterating expansion statements.
+constexpr int f3() {
+  static constexpr String s{"abcd"};
+  int count = 0;
+  template for (constexpr auto x : s) count++;
+  return count;
+}
+
+template <String s>
+constexpr int tf3() {
+  int count = 0;
+  template for (constexpr auto x : s) count++;
+  return count;
+}
+
+static_assert(f3() == 4);
+static_assert(tf3<"1">() == 1);
+static_assert(tf3<"12">() == 2);
+static_assert(tf3<"123">() == 3);
+static_assert(tf3<"1234">() == 4);
+
+void f4() {
+  static constexpr String empty{""};
+  static constexpr String s{"abcd"};
+  template for (auto x : empty) static_assert(false, "not expanded");
+  template for (constexpr auto x : s) g(x);
+  template for (auto x : s) g(x);
+}
+
+struct NegativeSize {
+  static constexpr const char* str = "123";
+  constexpr const char* begin() const { return str + 3; }
+  constexpr const char* end() const { return str; }
+};
+
+void negative_size() {
+  static constexpr NegativeSize n;
+  template for (auto x : n) g(x); // expected-error {{expansion statement size is not a constant expression}} \
+                                     old-interp-note {{constexpr evaluation hit maximum step limit}} \
+                                     new-interp-note {{cannot refer to element 5 of array of 4 elements in a constant expression}} \
+                                     expected-note {{in call to}}
+  template for (constexpr auto x : n) g(x); // expected-error {{expansion statement size is not a constant expression}} \
+                                               old-interp-note {{constexpr evaluation hit maximum step limit}} \
+                                               new-interp-note {{cannot refer to element 5 of array of 4 elements in a constant expression}} \
+                                               expected-note {{in call to}}
+}
+
+template <typename T, __SIZE_TYPE__ size>
+struct Array {
+  T data[size]{};
+  constexpr const T* begin() const { return data; }
+  constexpr const T* end() const { return data + size; }
+};
+
+struct NotInt {
+  struct iterator {};
+  constexpr iterator begin() const { return {}; }
+  constexpr iterator end() const { return {}; }
+};
+
+void not_int() {
+  static constexpr NotInt ni;
+  template for (auto x : ni) g(x); // expected-error {{invalid operands to binary expression}} \
+                                      expected-note {{while attempting to construct 'begin - begin' with iterator type 'iterator'}}
+}
+
+static constexpr Array<int, 3> integers{1, 2, 3};
+
+constexpr int friend_func();
+
+struct Private {
+  friend constexpr int friend_func();
+
+private:
+  constexpr const int* begin() const { return integers.begin(); } // expected-note 3 {{declared private here}}
+  constexpr const int* end() const { return integers.end(); } // expected-note 3 {{declared private here}}
+
+public:
+  static constexpr int member_func() {
+    int sum = 0;
+    static constexpr Private p1;
+    template for (auto x : p1) sum += x;
+    return sum;
+  }
+};
+
+struct Protected {
+  friend constexpr int friend_func();
+
+protected:
+  constexpr const int* begin() const { return integers.begin(); } // expected-note 3 {{declared protected here}}
+  constexpr const int* end() const { return integers.end(); } // expected-note 3 {{declared protected here}}
+
+public:
+  static constexpr int member_func() {
+    int sum = 0;
+    static constexpr Protected p1;
+    template for (auto x : p1) sum += x;
+    return sum;
+  }
+};
+
+void access_control() {
+  static constexpr Private p1;
+  template for (auto x : p1) g(x); // expected-error 3 {{'begin' is a private member of 'Private'}} expected-error 3 {{'end' is a private member of 'Private'}}
+
+  static constexpr Protected p2;
+  template for (auto x : p2) g(x); // expected-error 3 {{'begin' is a protected member of 'Protected'}} expected-error 3 {{'end' is a protected member of 'Protected'}}
+}
+
+constexpr int friend_func() {
+  int sum = 0;
+  static constexpr Private p1;
+  template for (auto x : p1) sum += x;
+
+  static constexpr Protected p2;
+  template for (auto x : p2) sum += x;
+  return sum;
+}
+
+static_assert(friend_func() == 12);
+static_assert(Private::member_func() == 6);
+static_assert(Protected::member_func() == 6);
+
+struct SizeNotICE {
+  struct iterator {
+    friend constexpr iterator operator+(iterator a, __PTRDIFF_TYPE__) { return a; }
+    friend constexpr __PTRDIFF_TYPE__ operator-(iterator, iterator) { return 7; }
+    constexpr iterator operator++() { return *this; }
+    int constexpr operator*() const { return 7; }
+
+    // NOT constexpr!
+    friend int operator!=(iterator, iterator) { return 7; } // expected-note {{declared here}}
+  };
+  constexpr iterator begin() const { return {}; }
+  constexpr iterator end() const { return {}; }
+};
+
+struct PlusMissing {
+  struct iterator {
+    friend constexpr __PTRDIFF_TYPE__ operator-(iterator, iterator) { return 7; }
+    constexpr iterator operator++() { return *this; }
+    int constexpr operator*() const { return 7; }
+  };
+  constexpr iterator begin() const { return {}; }
+  constexpr iterator end() const { return {}; }
+};
+
+struct DerefMissing {
+  struct iterator {
+    friend constexpr __PTRDIFF_TYPE__ operator-(iterator, iterator) { return 7; }
+    friend constexpr iterator operator+(iterator a, __PTRDIFF_TYPE__) { return a; }
+  };
+  constexpr iterator begin() const { return {}; }
+  constexpr iterator end() const { return {}; }
+};
+
+struct MinusMissing {
+  struct iterator {};
+  constexpr iterator begin() const { return {}; }
+  constexpr iterator end() const { return {}; }
+};
+
+void missing_funcs() {
+  static constexpr SizeNotICE s1;
+  static constexpr PlusMissing s2;
+  static constexpr DerefMissing s3;
+  static constexpr MinusMissing s4;
+
+  template for (auto x : s1) g(x); // expected-error {{expansion statement size is not a constant expression}} \
+                                      expected-note {{non-constexpr function 'operator!=' cannot be used in a constant expression}} \
+                                      expected-note {{in call to}}
+
+  template for (auto x : s2) g(x); // expected-error {{invalid operands to binary expression}}
+  template for (auto x : s3) g(x); // expected-error {{indirection requires pointer operand ('iterator' invalid)}}
+  template for (auto x : s4) g(x); // expected-error {{invalid operands to binary expression ('iterator' and 'iterator')}} \
+                                      expected-note {{while attempting to construct 'begin - begin' with iterator type 'iterator'}}
+}
+
+namespace adl {
+struct ADL {
+
+};
+
+constexpr const int* begin(const ADL&) { return integers.begin(); }
+constexpr const int* end(const ADL&) { return integers.end(); }
+}
+
+namespace adl_error {
+struct ADLError1 {
+  constexpr const int* begin() const { return integers.begin(); }
+};
+
+struct ADLError2 {
+  constexpr const int* end() const { return integers.end(); }
+};
+
+constexpr const int* begin(const ADLError2&) { return integers.begin(); }
+constexpr const int* end(const ADLError1&) { return integers.end(); }
+}
+
+namespace adl_both {
+static constexpr Array<int, 5> integers2{1, 2, 3, 4, 5};
+struct ADLBoth {
+  // Test that member begin/end are preferred over ADl begin/end. These return
+  // pointers to a different array.
+  constexpr const int* begin() const { return integers2.begin(); }
+  constexpr const int* end() const { return integers2.end(); }
+};
+
+constexpr const int* begin(const ADLBoth&) { return integers.begin(); }
+constexpr const int* end(const ADLBoth&) { return integers.end(); }
+}
+
+constexpr int adl_begin_end() {
+  static constexpr adl::ADL a;
+  int sum = 0;
+  template for (auto x : a) sum += x;
+  template for (constexpr auto x : a) sum += x;
+  return sum;
+}
+
+static_assert(adl_begin_end() == 12);
+
+void adl_mixed() {
+  static constexpr adl_error::ADLError1 a1;
+  static constexpr adl_error::ADLError2 a2;
+
+  // These are actually destructuring because there is no
+  // valid begin/end pair.
+  template for (auto x : a1) g(x);
+  template for (auto x : a2) g(x);
+}
+
+constexpr int adl_both_test() {
+  static constexpr adl_both::ADLBoth a;
+  int sum = 0;
+  template for (auto x : a) sum += x;
+  return sum;
+}
+
+static_assert(adl_both_test() == 15);
+#endif // 0
+
+struct A {};
+struct B { int x = 1; };
+struct C { int a = 1, b = 2, c = 3; };
+struct D {
+  int a = 1;
+  int* b = nullptr;
+  const char* c = "3";
+};
+
+struct Nested {
+  A a;
+  B b;
+  C c;
+};
+
+struct PrivateDestructurable {
+  friend void destructurable_friend();
+private:
+  int a, b; // expected-note 4 {{declared private here}}
+};
+
+struct ProtectedDestructurable {
+  friend void destructurable_friend();
+protected:
+  int a, b; // expected-note 4 {{declared protected here}}
+};
+
+void destructuring() {
+  static constexpr A a;
+  static constexpr B b;
+  static constexpr C c;
+  static constexpr D d;
+
+  template for (auto x : a) static_assert(false, "not expanded");
+  template for (constexpr auto x : a) static_assert(false, "not expanded");
+
+  template for (auto x : b) g(x);
+  template for (constexpr auto x : b) g(x);
+
+  template for (auto x : c) g(x);
+  template for (constexpr auto x : c) g(x);
+
+  template for (auto x : d) { // expected-note 2 {{in instantiation of expansion statement requested here}}
+    // expected-note@#g {{candidate function not viable: no known conversion from 'int *' to 'int' for 1st argument}}
+    // expected-note@#g {{candidate function not viable: no known conversion from 'const char *' to 'int' for 1st argument}}
+    g(x); // expected-error 2 {{no matching function for call to 'g'}}
+
+  }
+
+  template for (constexpr auto x : d) { // expected-note 2 {{in instantiation of expansion statement requested here}}
+    // expected-note@#g {{candidate function not viable: no known conversion from 'int *const' to 'int' for 1st argument}}
+    // expected-note@#g {{candidate function not viable: no known conversion from 'const char *const' to 'int' for 1st argument}}
+    g(x); // expected-error 2 {{no matching function for call to 'g'}}
+  }
+}
+
+constexpr int array() {
+  static constexpr int x[4]{1, 2, 3, 4};
+  int sum = 0;
+  template for (auto y : x) sum += y;
+  template for (constexpr auto y : x) sum += y;
+  return sum;
+}
+
+static_assert(array() == 20);
+
+template <auto v>
+constexpr int destructure() {
+  int sum = 0;
+  template for (auto x : v) sum += x;
+  template for (constexpr auto x : v) sum += x;
+  return sum;
+}
+
+static_assert(destructure<B{10}>() == 20);
+static_assert(destructure<C{}>() == 12);
+static_assert(destructure<C{3, 4, 5}>() == 24);
+
+constexpr int nested() {
+  static constexpr Nested n;
+  int sum = 0;
+  template for (constexpr auto x : n) {
+    static constexpr auto val = x;
+    template for (auto y : val) {
+      sum += y;
+    }
+  }
+  template for (constexpr auto x : n) {
+    static constexpr auto val = x;
+    template for (constexpr auto y : val) {
+      sum += y;
+    }
+  }
+  return sum;
+}
+
+static_assert(nested() == 14);
+
+void access_control_destructurable() {
+  template for (auto x : PrivateDestructurable()) {} // expected-error 2 {{cannot bind private member 'a' of 'PrivateDestructurable'}} \
+                                                        expected-error 2 {{cannot bind private member 'b' of 'PrivateDestructurable'}}
+
+  template for (auto x : ProtectedDestructurable()) {} // expected-error 2 {{cannot bind protected member 'a' of 'ProtectedDestructurable'}} \
+                                                          expected-error 2 {{cannot bind protected member 'b' of 'ProtectedDestructurable'}}
+
+  struct Derived : ProtectedDestructurable {
+    void f() {
+      template for (auto x : *this) {}
+    }
+  };
+}
+
+void destructurable_friend() {
+  template for (auto x : PrivateDestructurable()) {}
+  template for (auto x : ProtectedDestructurable()) {}
+}
+
+struct Placeholder {
+  A get_value() const { return {}; }
+  __declspec(property(get = get_value)) A a;
+};
+
+void placeholder() {
+  template for (auto x: Placeholder().a) {}
+}
+
+union Union { int a; long b;};
+
+struct MemberPtr {
+  void f() {}
+};
+
+void overload_set(int); // expected-note 2 {{possible target for call}}
+void overload_set(long); // expected-note 2 {{possible target for call}}
+
+void invalid_types() {
+  template for (auto x : void()) {} // expected-error {{cannot expand expression of incomplete type 'void'}}
+  template for (auto x : 1) {} // expected-error {{cannot expand expression of type 'int'}}
+  template for (auto x : 1.f) {} // expected-error {{cannot expand expression of type 'float'}}
+  template for (auto x : 'c') {} // expected-error {{cannot expand expression of type 'char'}}
+  template for (auto x : invalid_types) {} // expected-error {{cannot expand expression of type 'void ()'}}
+  template for (auto x : &invalid_types) {} // expected-error {{cannot expand expression of type 'void (*)()'}}
+  template for (auto x : &MemberPtr::f) {} // expected-error {{cannot expand expression of type 'void (MemberPtr::*)()'}}
+  template for (auto x : overload_set) {} // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}}
+  template for (auto x : &overload_set) {} // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}}
+  template for (auto x : nullptr) {} // expected-error {{cannot expand expression of type 'std::nullptr_t'}}
+  template for (auto x : __builtin_strlen) {} // expected-error {{builtin functions must be directly called}}
+  template for (auto x : Union()) {} // expected-error {{cannot expand expression of type 'Union'}}
+  template for (auto x : (char*)nullptr) {} // expected-error {{cannot expand expression of type 'char *'}}
+  template for (auto x : []{}) {} // expected-error {{cannot expand lambda closure type}}
+  template for (auto x : [x=3]{}) {} // expected-error {{cannot expand lambda closure type}}
+  template for (auto x : [](auto){}) {} // expected-error {{cannot expand lambda closure type}}
+  template for (auto x : []<typename>(){}) {} // expected-error {{cannot expand lambda closure type}}
+}
+
+constexpr int string_literals() {
+  int i = 0;
+  template for (auto x : "1234") i += int(x);
+  template for (auto x : L"1234") i += int(x);
+  template for (auto x : u"1234") i += int(x);
+  template for (auto x : U"1234") i += int(x);
+  template for (auto x : R"(1234)") i += int(x);
+  return i;
+}
+
+static_assert(string_literals() == 5 * (int('1') + int('2') + int('3') + int('4')));
+
+struct Bitfields {
+  int x : 5 = 1;
+  int y : 5 = 2;
+  int _ : 5 = 3;
+};
+
+constexpr int bitfields() {
+  int i = 0;
+  template for (auto x : Bitfields()) i += x;
+  return i;
+}
+
+static_assert(bitfields() == 6);
+
+struct EnumMember {
+  enum {
+    x, y, z
+  };
+
+  enum class Foo {
+    a, b, c
+  };
+
+  using enum Foo;
+};
+
+constexpr int enum_member() {
+  int i = 0;
+  template for (auto x : EnumMember()) i += x;
+  return i;
+}
+
+static_assert(enum_member() == 0);
+
+struct Mutable {
+  mutable int x{};
+};
+
+constexpr int mutable_member() {
+  const Mutable m;
+  template for (auto& i : m) i = 42;
+  return m.x;
+}
+
+static_assert(mutable_member() == 42);
+
+constexpr int vector_type() {
+  __attribute__((vector_size(sizeof(int) * 4))) int vec{1, 2, 3, 4};
+  int i = 0;
+  template for (auto x : vec) i += x;
+  return i;
+}
+
+static_assert(vector_type() == 10);
+
+constexpr int complex() {
+  _Complex int c{1, 2};
+  int i = 0;
+  template for (auto x : c) i += x;
+  return i;
+}
+
+static_assert(complex() == 3);
+
+#if 0 // Disabled until we support iterating expansion statements.
+struct BeginOnly {
+  int x{1};
+  constexpr const int* begin() const { return nullptr; }
+};
+
+struct EndOnly {
+  int x{2};
+  constexpr const int* end() const { return nullptr; }
+};
+
+namespace adl1 {
+struct BeginOnly {
+  int x{3};
+};
+constexpr const int* begin(const BeginOnly&) { return nullptr; }
+}
+
+namespace adl2 {
+struct EndOnly {
+  int x{4};
+};
+constexpr const int* end(const EndOnly&) { return nullptr; }
+}
+
+namespace adl3 {
+struct BeginOnlyDeleted {
+  int x{4};
+};
+constexpr const int* begin(const BeginOnlyDeleted&) = delete;
+}
+
+namespace adl4 {
+struct EndOnlyDeleted {
+  int x{4};
+};
+constexpr const int* end(const EndOnlyDeleted&) = delete;
+}
+
+namespace adl5 {
+struct BothDeleted {
+  int x{4};
+};
+constexpr const int* begin(const BothDeleted&) = delete; // expected-note {{candidate function has been explicitly deleted}}
+constexpr const int* end(const BothDeleted&) = delete;
+}
+
+namespace adl6 {
+struct BeginNotViable {
+  int x{4};
+};
+constexpr const int* begin(int) { return nullptr; }
+}
+
+namespace adl7 {
+struct EndNotViable {
+  int x{4};
+};
+constexpr const int* end(int) { return nullptr; }
+}
+
+namespace adl8 {
+struct BothNotViable {
+  int x{4};
+};
+constexpr const int* begin(int) { return nullptr; }
+constexpr const int* end(int) { return nullptr; }
+}
+
+namespace adl9 {
+struct BeginDeleted {
+  int x{4};
+};
+constexpr const int* begin(const BeginDeleted&) = delete; // expected-note {{candidate function has been explicitly deleted}}
+constexpr const int* end(const BeginDeleted&) { return nullptr; }
+}
+
+namespace adl10 {
+struct EndDeleted {
+  int x{4};
+};
+constexpr const int* begin(const EndDeleted&) { return nullptr; }
+constexpr const int* end(const EndDeleted&) = delete; // expected-note {{candidate function has been explicitly deleted}}
+}
+
+void unpaired_begin_end() {
+  static constexpr adl1::BeginOnly begin_only;
+  static constexpr adl2::EndOnly end_only;
+  static constexpr adl3::BeginOnlyDeleted begin_only_deleted;
+  static constexpr adl4::EndOnlyDeleted end_only_deleted;
+  static constexpr adl5::BothDeleted both_deleted;
+  static constexpr adl6::BeginNotViable begin_not_viable;
+  static constexpr adl7::EndNotViable end_not_viable;
+  static constexpr adl8::BothNotViable both_not_viable;
+  static constexpr adl9::BeginDeleted begin_deleted;
+  static constexpr adl10::EndDeleted end_deleted;
+
+  // Ok, these are destructuring because there is no valid pair.
+  template for (auto x : begin_only) {}
+  template for (auto x : begin_only_deleted) {}
+  template for (auto x : begin_not_viable) {}
+  template for (auto x : end_only) {}
+  template for (auto x : end_only_deleted) {}
+  template for (auto x : end_not_viable) {}
+
+  // This is also ok because overload resolution fails.
+  template for (auto x : both_not_viable) {}
+
+  // These are invalid because overload resolution succeeds (even though
+  // there is no usable begin() and/or end()).
+  template for (auto x : both_deleted) {} // expected-error {{call to deleted function 'begin'}} \
+                                             expected-note {{when looking up 'begin' function for range expression of type 'const adl5::BothDeleted'}}~
+
+  template for (auto x : begin_deleted) {} // expected-error {{call to deleted function 'begin'}} \
+                                              expected-note {{when looking up 'begin' function for range expression of type 'const adl9::BeginDeleted'}}
+
+  template for (auto x : end_deleted) {} // expected-error {{call to deleted function 'end'}} \
+                                            expected-note {{when looking up 'end' function for range expression of type 'const adl10::EndDeleted'}}
+}
+#endif // 0
+
+// Examples taken from [stmt.expand].
+namespace stmt_expand_examples {
+consteval int f(auto const&... Containers) {
+  int result = 0;
+  template for (auto const& c : {Containers...}) {      // OK, enumerating expansion statement
+    result += c[0];
+  }
+  return result;
+}
+constexpr int c1[] = {1, 2, 3};
+constexpr int c2[] = {4, 3, 2, 1};
+static_assert(f(c1, c2) == 5);
+
+#if 0 // Disabled until we support iterating expansion statements.
+// TODO: This entire example should work without issuing any diagnostics once
+// we have full support for references to constexpr variables (P2686).
+consteval int f() {
+  constexpr Array<int, 3> arr {1, 2, 3}; // expected-note{{add 'static' to give it a constant address}} \
+                                            new-interp-note 5 {{add 'static' to give it a constant address}}
+
+  int result = 0;
+
+  // expected-error@#invalid-ref {{constexpr variable '__range1' must be initialized by a constant expression}}
+  // expected-error@#invalid-ref {{constexpr variable '__begin1' must be initialized by a constant expression}}
+  // expected-error@#invalid-ref {{constexpr variable '__end1' must be initialized by a constant expression}}
+  // expected-note@#invalid-ref {{reference to 'arr' is not a constant expression}}
+  // old-interp-error@#invalid-ref {{expansion statement size is not a constant expression}}
+  // old-interp-note@#invalid-ref {{in call to}}
+  // old-interp-note@#invalid-ref 3 {{member call on variable '__range1' whose value is not known}}
+  // old-interp-note@#invalid-ref 3 {{declared here}}
+  // new-interp-error@#invalid-ref 3 {{constexpr variable '__iter1' must be initialized by a constant expression}}
+  // new-interp-note@#invalid-ref 5 {{pointer to subobject of 'arr' is not a constant expression}}
+  // new-interp-note@#invalid-ref 3 {{in instantiation of expansion statement}}
+  template for (constexpr int s : arr) { // #invalid-ref                // OK, iterating expansion statement
+    result += sizeof(char[s]);
+  }
+  return result;
+}
+static_assert(f() == 6); // old-interp-error {{static assertion failed due to requirement 'f() == 6'}} old-interp-note {{expression evaluates to '0 == 6'}}
+#endif // 0
+
+struct S {
+  int i;
+  short s;
+};
+
+consteval long f(S s) {
+  long result = 0;
+  template for (auto x : s) {                           // OK, destructuring expansion statement
+    result += sizeof(x);
+  }
+  return result;
+}
+static_assert(f(S{}) == sizeof(int) + sizeof(short));
+}
+
+void not_constant_expression() {
+  template for (constexpr auto x : B()) { // expected-error {{constexpr variable '[__u0]' must be initialized by a constant expression}} \
+                                             expected-note {{reference to temporary is not a constant expression}} \
+                                             expected-note {{temporary created here}} \
+                                             expected-error {{constexpr variable 'x' must be initialized by a constant expression}} \
+                                             expected-note {{in instantiation of expansion statement requested here}} \
+                                             old-interp-note {{read of variable '[__u0]' whose value is not known}} \
+                                             old-interp-note {{declared here}} \
+                                             new-interp-note {{cannot access field of null pointer}}
+    g(x);
+  }
+}
+
+constexpr int references_enumerating() {
+  int x = 1, y = 2, z = 3;
+  template for (auto& x : {x, y, z}) { ++x; }
+  template for (auto&& x : {x, y, z}) { ++x; }
+  return x + y + z;
+}
+
+static_assert(references_enumerating() == 12);
+
+constexpr int references_destructuring() {
+  C c;
+  template for (auto& x : c) { ++x; }
+  template for (auto&& x : c) { ++x; }
+  return c.a + c.b + c.c;
+}
+
+static_assert(references_destructuring() == 12);
+
+constexpr int break_continue() {
+  int sum = 0;
+  template for (auto x : {1, 2}) {
+    break;
+    sum += x;
+  }
+
+  template for (auto x : {3, 4}) {
+    continue;
+    sum += x;
+  }
+
+  template for (auto x : {5, 6}) {
+    if (x == 6) break;
+    sum += x;
+  }
+
+  template for (auto x : {7, 8, 9}) {
+    if (x == 8) continue;
+    sum += x;
+  }
+
+  return sum;
+}
+
+static_assert(break_continue() == 21);
+
+constexpr int break_continue_nested() {
+  int sum = 0;
+
+  template for (auto x : {1, 2}) {
+    template for (auto y : {3, 4}) {
+      if (x == 2) break;
+      sum += y;
+    }
+    sum += x;
+  }
+
+  template for (auto x : {5, 6}) {
+    template for (auto y : {7, 8}) {
+      if (x == 6) continue;
+      sum += y;
+    }
+    sum += x;
+  }
+
+  return sum;
+}
+
+static_assert(break_continue_nested() == 36);
+
+template <typename ...Ts>
+void unexpanded_pack_bad(Ts ...ts) {
+  template for (auto x : ts) {} // expected-error {{expression contains unexpanded parameter pack 'ts'}}
+  template for (Ts x : {1, 2}) {} // expected-error {{declaration type contains unexpanded parameter pack 'Ts'}}
+  template for (auto x : {ts}) {} // expected-error {{initializer contains unexpanded parameter pack}} \
+  // expected-note {{in instantiation of expansion statement requested here}}
+}
+
+struct E { int x, y; constexpr E(int x, int y) : x{x}, y{y} {}};
+
+template <typename ...Es>
+constexpr int unexpanded_pack_good(Es ...es) {
+  int sum = 0;
+  ([&] {
+    template for (auto x : es) sum += x;
+    template for (Es e : {{5, 6}, {7, 8}}) sum += e.x + e.y;
+  }(), ...);
+  return sum;
+}
+
+static_assert(unexpanded_pack_good(E{1, 2}, E{3, 4}) == 62);
+
+// Ensure that the expansion-initializer is evaluated even if it expands
+// to nothing.
+//
+// This is related to CWG 3048. Note that we currently still model this as
+// a DecompositionDecl w/ zero bindings.
+constexpr bool empty_side_effect() {
+  struct A {
+    constexpr A(bool& b) {
+      b = true;
+    }
+  };
+
+  bool constructed = false;
+  template for (auto x : A(constructed)) static_assert(false);
+  return constructed;
+}
+
+static_assert(empty_side_effect());
+
+namespace apply_lifetime_extension {
+struct T {
+  int& x;
+  constexpr T(int& x) noexcept : x(x) {}
+  constexpr ~T() noexcept { x = 42; }
+};
+
+constexpr const T& f(const T& t) noexcept { return t; }
+constexpr T g(int& x) noexcept { return T(x); }
+
+// CWG 3043:
+//
+// Lifetime extension only applies to destructuring expansion statements
+// (enumerating statements don't have a range variable, and the range variable
+// of iterating statements is constexpr).
+constexpr int lifetime_extension() {
+  int x = 5;
+  int sum  = 0;
+  template for (auto e : f(g(x))) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+template <typename T>
+constexpr int lifetime_extension_instantiate_expansions() {
+  int x = 5;
+  int sum  = 0;
+  template for (T e : f(g(x))) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+template <typename T>
+constexpr int lifetime_extension_dependent_expansion_stmt() {
+  int x = 5;
+  int sum  = 0;
+  template for (int e : f(g((T&)x))) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+template <typename U>
+struct foo {
+  template <typename T>
+  constexpr int lifetime_extension_multiple_instantiations() {
+      int x = 5;
+      int sum  = 0;
+      template for (T e : f(g((U&)x))) {
+        sum += x;
+      }
+      return sum + x;
+  }
+};
+
+static_assert(lifetime_extension() == 47);
+static_assert(lifetime_extension_instantiate_expansions<int>() == 47);
+static_assert(lifetime_extension_dependent_expansion_stmt<int>() == 47);
+static_assert(foo<int>().lifetime_extension_multiple_instantiations<int>() == 47);
+}
+
+template <typename... Ts>
+constexpr int return_from_expansion(Ts... ts) {
+  template for (int i : {1, 2, 3}) {
+    return (ts + ...);
+  }
+  __builtin_unreachable();
+}
+
+static_assert(return_from_expansion(4, 5, 6) == 15);
+
+void not_constexpr();
+
+constexpr int empty_expansion_consteval() {
+  template for (auto _ : {}) {
+    not_constexpr();
+  }
+  return 3;
+}
+
+static_assert(empty_expansion_consteval() == 3);
+
+void nested_empty_expansion() {
+  template for (auto x1 : {})
+    template for (auto x2 : {1})
+      static_assert(false);
+
+  template for (auto x1 : {1})
+    template for (auto x2 : {})
+      template for (auto x3 : {1})
+        static_assert(false);
+
+  template for (auto x1 : {})
+    template for (auto x2 : {})
+      template for (auto x3 : {})
+        template for (auto x4 : {1})
+          static_assert(false);
+
+  template for (auto x1 : {})
+    template for (auto x2 : {1})
+      template for (auto x3 : {})
+        template for (auto x4 : {1})
+          static_assert(false);
+
+  template for (auto x1 : {})
+    template for (auto x2 : {1})
+      template for (auto x4 : {1})
+        static_assert(false);
+}
+
+struct Empty {};
+
+template <typename T>
+void nested_empty_expansion_dependent() {
+  template for (auto x1 : T())
+    template for (auto x2 : {1})
+      static_assert(false);
+
+  template for (auto x1 : {1})
+    template for (auto x2 : T())
+      template for (auto x3 : {1})
+        static_assert(false);
+
+  template for (auto x1 : T())
+    template for (auto x2 : T())
+      template for (auto x3 : T())
+        template for (auto x4 : {1})
+          static_assert(false);
+
+  template for (auto x1 : T())
+    template for (auto x2 : {1})
+      template for (auto x3 : T())
+        template for (auto x4 : {1})
+          static_assert(false);
+
+  template for (auto x1 : T())
+    template for (auto x2 : {1})
+      template for (auto x4 : {1})
+        static_assert(false);
+}
+
+void nested_empty_expansion_dependent_instantiate() {
+  nested_empty_expansion_dependent<Empty>();
+}
+
+// Destructuring expansion statements using tuple_size/tuple_element/get.
+namespace std {
+template <typename>
+struct tuple_size;
+
+template <__SIZE_TYPE__, typename>
+struct tuple_element; // expected-note {{template is declared here}}
+
+namespace get_decomposition {
+struct MemberGet {
+  int x[6]{};
+
+  template <__SIZE_TYPE__ I>
+  constexpr int& get() { return x[I * 2]; }
+};
+
+struct ADLGet {
+  long x[8]{};
+};
+
+template <__SIZE_TYPE__ I>
+constexpr long& get(ADLGet& a) { return a.x[I * 2]; }
+} // namespace get_decomposition
+
+template <>
+struct tuple_size<get_decomposition::MemberGet> {
+  static constexpr __SIZE_TYPE__ value = 3;
+};
+
+template <__SIZE_TYPE__ I>
+struct tuple_element<I, get_decomposition::MemberGet> {
+  using type = int;
+};
+
+template <>
+struct tuple_size<get_decomposition::ADLGet> {
+  static constexpr __SIZE_TYPE__ value = 4;
+};
+
+template <__SIZE_TYPE__ I>
+struct tuple_element<I, get_decomposition::ADLGet> {
+  using type = long;
+};
+
+constexpr int member() {
+  get_decomposition::MemberGet m;
+  int v = 1;
+  template for (int& i : m) {
+    i = v;
+    v++;
+  }
+  return m.x[0] + m.x[2] + m.x[4];
+}
+
+constexpr long adl() {
+  get_decomposition::ADLGet m;
+  long v = 1;
+  template for (long& i : m) {
+    i = v;
+    v++;
+  }
+  return m.x[0] + m.x[2] + m.x[4] + m.x[6];
+}
+
+static_assert(member() == 6);
+static_assert(adl() == 10);
+
+struct TupleSizeOnly {};
+
+template <>
+struct tuple_size<TupleSizeOnly> {
+  static constexpr __SIZE_TYPE__ value = 3;
+};
+
+struct TupleSizeAndGet {
+  template <__SIZE_TYPE__>
+  constexpr int get() { return 1; }
+};
+
+template <>
+struct tuple_size<TupleSizeAndGet> {
+  static constexpr __SIZE_TYPE__ value = 3;
+};
+
+void invalid() {
+  template for (auto x : TupleSizeOnly()) {} // expected-error {{use of undeclared identifier 'get'}} \
+                                                expected-note {{in implicit initialization of binding declaration}}
+
+  template for (auto x : TupleSizeAndGet()) {} // expected-error {{implicit instantiation of undefined template 'std::tuple_element<0, std::TupleSizeAndGet>'}} \
+                                                  expected-note {{in implicit initialization of binding declaration}}
+}
+} // namespace std
+
+constexpr int generic_lambda() {
+  static constexpr int arr[]{1, 2, 3};
+  int sum = 0;
+  [n = 5, &sum]<class = void>() {
+    template for (constexpr auto x : arr) {
+      sum += n + x;
+    }
+  }();
+  return sum;
+}
+
+static_assert(generic_lambda() == 21);
+
+void for_range_decl_must_be_var() {
+  template for (void q() : "error") // expected-error {{expansion statement declaration must declare a variable}}
+    ;
+}
+
+void init_list_bad() {
+  template for (auto y : {{1}, {2}, {3, {4}}, {{{5}}}}); // expected-error {{cannot deduce actual type for variable 'y' with type 'auto' from initializer list}} \
+                                                            expected-note {{in instantiation of expansion statement requested here}}
+}
+
+// Test that the init statement is evaluated even if the expansion statement
+// expands to nothing.
+constexpr int init_stmt_empty_expansion() {
+#if 0 // Disabled until we support iterating expansion statements.
+  static constexpr String empty{""};
+#endif // 0
+  int x = 0;
+  template for (int _ = x += 1; auto i : {}) {}
+#if 0 // Disabled until we support iterating expansion statements.
+  template for (int _ = x += 2; auto i : empty) {}
+#endif // 0
+  template for (int _ = x += 3; auto i : Empty()) {}
+  return x;
+}
+
+static_assert(init_stmt_empty_expansion() == 4);
+
+void vla(int n) {
+  int a[n];
+  template for (int x : a) {} // expected-error {{cannot expand variable length array type 'int[n]'}}
+}
+
+template <typename T>
+void template_vla(T& a) { // expected-note {{variably modified type 'int[n]' cannot be used as a template argument}}
+  template for (int x : a) {}
+}
+
+void instantiate_template_vla(int n) {
+  int a[n];
+  template_vla(a); // expected-error {{no matching function for call to 'template_vla'}}
+}
+
+struct Incomplete; // expected-note 2 {{forward declaration of 'Incomplete'}}
+void incomplete_type(Incomplete& s) {
+  template for (int x : s) {} // expected-error {{cannot expand expression of incomplete type 'Incomplete'}}
+}
+
+template <typename T>
+void dependent_incomplete_type(T& s) {
+  template for (int x : s) {} // expected-error {{cannot expand expression of incomplete type 'Incomplete'}}
+}
+
+template void dependent_incomplete_type<Incomplete>(Incomplete&); // expected-note {{in instantiation of function template specialization 'dependent_incomplete_type<Incomplete>' requested here}}
+
+template <typename T>
+void lambda_template(T a) {
+  template for (auto x : a) {} // expected-error {{cannot expand lambda closure type}}
+}
+
+void lambda_template_call() {
+  lambda_template([]{}); // expected-note {{in instantiation of function template specialization}}
+}
+
+#if 0 // Disabled until we support iterating expansion statements.
+// CWG 3131 makes it possible to expand over non-constexpr ranges.
+namespace cwg3131 {
+constexpr int f1() {
+  int j = 0;
+  template for (auto i : Array<int, 3>{1, 2, 3}) j +=i;
+  return j;
+}
+
+constexpr int f2() {
+  Array<int, 3> a{1, 2, 3};
+  int j = 0;
+  template for (auto i : a) j +=i; // new-interp-error {{expansion statement size is not a constant expression}} \
+                                      new-interp-note {{initializer of '__range1' is not a constant expression}} \
+                                      new-interp-note {{declared here}}
+  return j;
+}
+
+static_assert(f1() == 6);
+static_assert(f2() == 6); // new-interp-error {{static assertion failed due to requirement 'f2() == 6'}} \
+                             new-interp-note {{expression evaluates to '0 == 6'}}
+
+template <typename T>
+struct Span {
+  T* data;
+  __SIZE_TYPE__ size;
+
+  template <__SIZE_TYPE__ N>
+  constexpr Span(T(&a)[N]) : data{a}, size{N} {}
+
+  constexpr auto begin() const -> T* { return data; }
+  constexpr auto end() const -> T* { return data + size; }
+};
+
+constexpr int arr[3] = { 1, 2, 3 };
+consteval Span<const int> foo() {
+  return Span<const int>(arr);
+}
+
+constexpr int f3() {
+  int r = 0;
+  template for (constexpr auto m : foo())
+    r += m;
+  return r;
+}
+
+static_assert(f3() == 6);
+}
+
+// Test that we actually do 'begin + decltype(begin - begin){i}'.
+namespace cwg3044 {
+struct DifferenceType {
+  int v;
+  explicit constexpr DifferenceType(__PTRDIFF_TYPE__ v) : v(int(v)) {}
+};
+
+struct Range {
+  struct iterator {
+    DifferenceType v;
+
+    constexpr iterator& operator++() {
+      v.v++;
+      return *this;
+    }
+
+    constexpr int operator*() const { return int(v.v); }
+    friend constexpr bool operator!=(iterator a, iterator b) { return a.v.v != b.v.v; }
+    friend constexpr iterator operator+(iterator a, DifferenceType b) {
+      return iterator{DifferenceType{int(a.v.v) + int(b.v)}};
+    }
+    friend constexpr DifferenceType operator-(iterator a, iterator b) {
+      return DifferenceType{int(a.v.v) - int(b.v.v)};
+    }
+  };
+
+  constexpr auto begin() const { return iterator{DifferenceType{1}}; }
+  constexpr auto end() const { return iterator{DifferenceType{5}}; }
+};
+
+constexpr int f() {
+  int val = 0;
+  template for (auto v : Range()) val += v;
+  template for (constexpr auto v : Range()) val += v;
+  return val;
+}
+
+static_assert(f() == 20);
+}
+
+// Test that 'iter' is an lvalue, because we used to not actually create a
+// variable for it.
+namespace cwg3140 {
+struct IterLValue {
+  struct iterator {
+    int v;
+
+    constexpr iterator& operator++() {
+      v++;
+      return *this;
+    }
+
+    constexpr int operator*() const & { return int(v); }
+    constexpr int operator*() const && = delete("'iter' must be an lvalue");
+    friend constexpr bool operator!=(iterator a, iterator b) { return a.v != b.v; }
+    friend constexpr iterator operator+(iterator a, int b) { return iterator{a.v + b}; }
+    friend constexpr int operator-(iterator a, iterator b) { return a.v - b.v; }
+  };
+
+  constexpr auto begin() const { return iterator{1}; }
+  constexpr auto end() const { return iterator{5}; }
+};
+
+constexpr int f() {
+  int val = 0;
+  template for (auto v : IterLValue()) val += v;
+  template for (constexpr auto v : IterLValue()) val += v;
+  return val;
+}
+
+static_assert(f() == 20);
+}
+#endif // 0
+
+namespace cwg3149 {
+struct NotCopyable {
+  int x;
+  constexpr NotCopyable(int x) : x{x} {}
+  constexpr NotCopyable(NotCopyable&& o) : x{o.x} {}
+  NotCopyable(const NotCopyable&) = delete; // expected-note 2 {{explicitly marked deleted here}}
+};
+
+struct NotMovable {
+  int x;
+  constexpr NotMovable(int x) : x{x} {}
+  constexpr NotMovable(const NotMovable& o) : x{o.x} {}
+  NotMovable(NotMovable&&) = delete; // expected-note 2 {{explicitly marked deleted here}}
+};
+
+template <typename T>
+struct Wrapper {
+  T a;
+  T b;
+};
+
+constexpr int f() {
+  Wrapper<NotMovable> nm{{3},{4}};
+  Wrapper<NotCopyable> nc{{5}, {6}};
+  int sum = 0;
+  template for (auto x : Wrapper<NotCopyable>{{1}, {2}}) sum += x.x;
+  template for (auto& x : nc) sum += x.x;
+  template for (auto x : static_cast<Wrapper<NotCopyable>&&>(nc)) sum += x.x;
+  template for (auto&& x : static_cast<Wrapper<NotMovable>&&>(nm)) sum += x.x;
+  return sum;
+}
+
+static_assert(f() == 32);
+
+int err() {
+  Wrapper<NotCopyable> nc{{3},{4}};
+  int sum = 0;
+
+  // expected-error at +2 2 {{call to deleted constructor of 'cwg3149::NotMovable'}}
+  // expected-note at +1 2 {{in instantiation of expansion statement requested here}}
+  template for (auto x : Wrapper<NotMovable>{{1}, {2}}) sum += x.x;
+
+  // expected-error at +2 2 {{call to deleted constructor of 'cwg3149::NotCopyable'}}
+  // expected-note at +1 2 {{in instantiation of expansion statement requested here}}
+  template for (auto x : nc) sum += x.x;
+  return sum;
+}
+}
+
+namespace cwg3045 {
+void f1() {
+  int x;
+  int y;
+
+  template for (auto x : {1}) { // expected-note {{previous definition is here}}
+    int x{}; // expected-error {{redefinition of 'x'}}
+  }
+
+  { int x; }
+
+  template for (auto x : {1}) {
+    { int x{}; }
+    {
+      int x{};
+      { int x{}; }
+    }
+  }
+
+  template for (auto x : {1}) {
+    template for (auto y : {1}) { // expected-note {{previous definition is here}}
+      int x{};
+      int y{}; // expected-error {{redefinition of 'y'}}
+    }
+  }
+}
+
+void f2(int q) {
+  switch (q) {
+    case 1: template for (auto x : {1}) [[fallthrough]]; // expected-error {{fallthrough annotation does not directly precede switch label}}
+  }
+  switch (q) {
+    case 1: template for (auto x : {1}) [[fallthrough]]; // expected-error {{fallthrough annotation does not directly precede switch label}}
+    case 2:;
+  }
+
+  switch (q) {
+    case 1: template for (auto x : {1}) { [[fallthrough]]; } // expected-error {{fallthrough annotation does not directly precede switch label}}
+  }
+
+  switch (q) {
+    case 1: template for (auto x : {1}) {
+      switch (q) {
+        case 1: [[fallthrough]];
+        case 2:;
+      }
+    }
+  }
+}
+}
+
+#if 0 // Disabled until we support iterating expansion statements.
+// Check that we apply lifetime extension to iterating expansions statements.
+//
+// The new constant interpreter erroring on this is likely https://github.com/llvm/llvm-project/issues/187775.
+namespace cwg3140 {
+constexpr const char* arr = "1";
+struct T {
+  int& x;
+  constexpr T(int& x) noexcept : x(x) {}
+  constexpr ~T() noexcept { x = 42; }
+  constexpr const char* begin() const { return arr; }
+  constexpr const char* end() const { return arr + 1; }
+};
+
+constexpr const T& f(const T& t) noexcept { return t; }
+constexpr T g(int& x) noexcept { return T(x); }
+
+constexpr int lifetime_extension_iterating() {
+  int x = 5;
+  int sum  = 0;
+  // new-interp-error at +3 {{expansion statement size is not a constant expression}}
+  // new-interp-note at +2 {{initializer of '__range1' is not a constant expression}}
+  // new-interp-note at +1 {{declared here}}
+  template for (auto e : f(g(x))) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+template <typename T>
+constexpr int lifetime_extension_iterating_dependent() {
+  int x = 5;
+  int sum  = 0;
+  // new-interp-error at +3 {{expansion statement size is not a constant expression}}
+  // new-interp-note at +2 {{initializer of '__range0' is not a constant expression}}
+  // new-interp-note at +1 {{declared here}}
+  template for (auto e : T(f(g(x)))) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+template <typename T>
+constexpr int lifetime_extension_iterating_instantiation() {
+  int x = 5;
+  int sum  = 0;
+  // new-interp-error at +3 {{expansion statement size is not a constant expression}}
+  // new-interp-note at +2 {{initializer of '__range2' is not a constant expression}}
+  // new-interp-note at +1 {{declared here}}
+  template for (auto e : f(g(x))) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+static_assert(lifetime_extension_iterating() == 47); // new-interp-error {{static assertion failed}} new-interp-note {{expression evaluates to}}
+static_assert(lifetime_extension_iterating_dependent<const T&>() == 47); // new-interp-error {{static assertion expression is not an integral constant expression}} new-interp-note {{in instantiation of}}
+static_assert(lifetime_extension_iterating_instantiation<void>() == 47); // new-interp-error {{static assertion expression is not an integral constant expression}} new-interp-note {{in instantiation of}}
+}
+#endif // 0
+
+// Tests that make sure that certain parts of an expansion statement end up in
+// the right DeclContext.
+namespace decl_context {
+struct S { int x{}, y{}; };
+
+void dependent_context() {
+  // The init-statement should never be in a dependent context.
+  template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {} // expected-error {{static assertion failed}}
+  template for (({ static_assert(false); }); auto x : {}) {} // expected-error {{static assertion failed}}
+
+  // Likewise, the expansion-initializer is not dependent.
+  template for (auto x : { ({ static_assert(false); 4; }) }) {} // expected-error {{static assertion failed}}
+  template for (auto x : ({ static_assert(false); S(); })) {} // expected-error {{static assertion failed}}
+
+  // The for-range-declaration *is* dependent because it only appears within the
+  // expansion(s), which means that it is discarded entirely if the expansion
+  // size is 0.
+  template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+  template for (decltype(({ static_assert(false); 42; })) _ : {1}) {} // expected-error {{static assertion failed}} expected-note {{in instantiation of expansion statement}}
+}
+
+template <typename>
+void not_instantiated() {
+  template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {}
+  template for (({ static_assert(false); }); auto x : {}) {}
+  template for (auto x : { ({ static_assert(false); 4; }) }) {}
+  template for (auto x : ({ static_assert(false); S(); })) {}
+  template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+  template for (decltype(({ static_assert(false); 42; })) _ : {1}) {}
+}
+
+template <typename>
+void instantiated() {
+  template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {} // expected-error {{static assertion failed}}
+  template for (({ static_assert(false); }); auto x : {}) {} // expected-error {{static assertion failed}}
+  template for (auto x : { ({ static_assert(false); 4; }) }) {} // expected-error {{static assertion failed}}
+  template for (auto x : ({ static_assert(false); S(); })) {} // expected-error {{static assertion failed}}
+  template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+  template for (decltype(({ static_assert(false); 42; })) _ : {1}) {} // expected-error {{static assertion failed}}
+}
+
+template <typename>
+struct Template {
+  template <typename>
+  void not_fully_instantiated() {
+    template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {}
+    template for (({ static_assert(false); }); auto x : {}) {}
+    template for (auto x : { ({ static_assert(false); 4; }) }) {}
+    template for (auto x : ({ static_assert(false); S(); })) {}
+    template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+    template for (decltype(({ static_assert(false); 42; })) _ : {1}) {}
+  }
+
+  template <typename>
+  void instantiated() {
+    template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {} // expected-error {{static assertion failed}}
+    template for (({ static_assert(false); }); auto x : {}) {} // expected-error {{static assertion failed}}
+    template for (auto x : { ({ static_assert(false); 4; }) }) {} // expected-error {{static assertion failed}}
+    template for (auto x : ({ static_assert(false); S(); })) {} // expected-error {{static assertion failed}}
+    template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+    template for (decltype(({ static_assert(false); 42; })) _ : {1}) {} // expected-error {{static assertion failed}}
+  }
+};
+
+void f() {
+  instantiated<void>(); // expected-note {{in instantiation of function template specialization 'decl_context::instantiated<void>'}}
+
+  // This should *not* produce any diagnostics.
+  Template<int>();
+
+  // Rather, the diagnostics are only emitted if we actually fully instantiate
+  // the function template.
+  Template<void>().instantiated<void>(); // expected-note {{in instantiation of function template specialization 'decl_context::Template<void>::instantiated<void>'}}
+}
+}
diff --git a/clang/test/SemaTemplate/GH176155.cpp b/clang/test/SemaTemplate/GH176155.cpp
index 12a9de2ae2d46..ce2ca2d488a03 100644
--- a/clang/test/SemaTemplate/GH176155.cpp
+++ b/clang/test/SemaTemplate/GH176155.cpp
@@ -1,26 +1,16 @@
 // RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+// expected-no-diagnostics
 
 template <int> struct bad {
   template <class T, auto =
-                         [] { // #lambda
-                           // expected-note@#lambda {{while substituting into a lambda expression here}}
-                           // expected-note@#lambda 2{{capture 'i' by value}}
-                           // expected-note@#lambda 2{{capture 'i' by reference}}
-                           // expected-note@#lambda 2{{default capture by value}}
-                           // expected-note@#lambda 2{{default capture by reference}}
-                           for (int i = 0; i < 100; ++i) { // #i
-                             // expected-error at -1 {{variable 'i' cannot be implicitly captured in a lambda with no capture-default specified}}
-                             // expected-note@#i {{'i' declared here}}
-                             // expected-note@#lambda {{lambda expression begins here}}
-                             // expected-error at -4 {{variable 'i' cannot be implicitly captured in a lambda with no capture-default specified}}
-                             // expected-note@#i {{'i' declared here}}
-                             // expected-note@#lambda {{lambda expression begins here}}
+                         [] {
+                           for (int i = 0; i < 100; ++i) {
                              struct LoopHelper {
                                static constexpr void process() {}
                              };
                            }
                          }>
-  static void f(T) {} // expected-note {{in instantiation of default argument for 'f<int>' required here}}
+  static void f(T) {}
 };
 
-int main() { bad<0>::f(0); } // expected-note {{while substituting deduced template arguments into function template 'f'}}
+int main() { bad<0>::f(0); }
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 2c834b07f9a8f..9ff43c713d5b3 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -320,7 +320,13 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td>Expansion Statements</td>
   <td><a href="https://wg21.link/P1306">P1306R5</a></td>
-  <td class="none" align="center">No</td>
+  <td class="partial" align="center">
+    <details>
+      <summary>Clang 23 (Partial)</summary>
+      Iterating expansion statements currently cannot be expanded and will
+      result in a diagnostic, but other types of expansion statements work.
+    </details>
+  </td>
  </tr>
  <tr>
    <td>constexpr virtual inheritance</td>

>From a52248a5f7f5a122914636990c0ce7ce19f32f54 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 10 Jul 2026 22:35:33 +0200
Subject: [PATCH 3/5] Fix patch after changes in main

---
 clang/lib/Sema/SemaExpand.cpp                | 8 +++++---
 clang/lib/Sema/SemaStmt.cpp                  | 1 -
 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp | 3 ++-
 3 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index 8647276149d9a..b95612d08fb10 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -266,7 +266,7 @@ static StmtResult BuildDestructuringDecompositionDecl(
   TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(AutoRRef);
   auto *DD =
       DecompositionDecl::Create(S.Context, S.CurContext, ColonLoc, ColonLoc,
-                                AutoRRef, TSI, SC_Auto, Bindings);
+                                ColonLoc, AutoRRef, TSI, SC_Auto, Bindings);
 
   if (VarIsConstexpr)
     DD->setConstexpr(true);
@@ -307,7 +307,8 @@ Sema::BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc,
 ExprResult Sema::ActOnCXXExpansionInitList(MultiExprArg SubExprs,
                                            SourceLocation LBraceLoc,
                                            SourceLocation RBraceLoc) {
-  return new (Context) InitListExpr(Context, LBraceLoc, SubExprs, RBraceLoc);
+  return new (Context) InitListExpr(Context, LBraceLoc, SubExprs, RBraceLoc,
+                                    /*IsExplicit=*/true);
 }
 
 StmtResult Sema::ActOnCXXExpansionStmtPattern(
@@ -476,7 +477,8 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
   }
 
   ExprResult Select = BuildCXXExpansionSelectExpr(
-      new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc),
+      new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc,
+                                 /*IsExplicit=*/false),
       Index);
 
   if (Select.isInvalid()) {
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index c2f0e59f5192e..a1da5e54d4980 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2979,7 +2979,6 @@ StmtResult Sema::BuildCXXForRangeStmt(
           RangeVar->getInit(), RangeVar->getLocation(), RParenLoc);
     };
 
-    auto BeginRangeRefTy = RangeVar->getType().getNonReferenceType();
     ForRangeBeginEndInfo ForRangeInfo = BuildCXXForRangeBeginEndVars(
         S, RangeVar, ColonLoc, CoawaitLoc, LifetimeExtendTemps, Kind,
         /*Constexpr=*/false, &RebuildResult, RebuildWithDereference);
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
index f98ddd0fa949d..84656396361e3 100644
--- a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
@@ -798,7 +798,8 @@ void not_constant_expression() {
                                              expected-note {{in instantiation of expansion statement requested here}} \
                                              old-interp-note {{read of variable '[__u0]' whose value is not known}} \
                                              old-interp-note {{declared here}} \
-                                             new-interp-note {{cannot access field of null pointer}}
+                                             new-interp-note {{temporary created here}} \
+                                             new-interp-note {{read of temporary is not allowed in a constant expression outside the expression that created the temporary}}
     g(x);
   }
 }

>From 8aebca395d933f0f93f2d8fc06a33ebb26a37dda Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 10 Jul 2026 22:37:50 +0200
Subject: [PATCH 4/5] clang-format

---
 clang/include/clang/AST/DeclTemplate.h    |  4 +---
 clang/include/clang/AST/ExprCXX.h         |  4 +---
 clang/include/clang/AST/StmtCXX.h         |  7 ++++---
 clang/include/clang/Parse/Parser.h        |  2 +-
 clang/include/clang/Sema/Scope.h          |  4 +---
 clang/lib/AST/ASTImporter.cpp             |  8 ++++----
 clang/lib/AST/ByteCode/Compiler.h         |  1 +
 clang/lib/AST/ExprCXX.cpp                 |  4 ++--
 clang/lib/AST/StmtCXX.cpp                 |  7 +++----
 clang/lib/AST/StmtPrinter.cpp             |  3 +--
 clang/lib/Parse/ParseDecl.cpp             |  2 +-
 clang/lib/Parse/ParseStmt.cpp             | 12 +++++-------
 clang/lib/Sema/SemaExpand.cpp             |  4 ++--
 clang/lib/Sema/SemaStmt.cpp               | 11 +++++------
 clang/lib/Sema/TreeTransform.h            |  2 +-
 clang/lib/Serialization/ASTReaderStmt.cpp |  3 +--
 clang/lib/Serialization/ASTWriterStmt.cpp |  5 ++---
 17 files changed, 36 insertions(+), 47 deletions(-)

diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 3f893ecd567ca..ed1a517247943 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3434,9 +3434,7 @@ class CXXExpansionStmtDecl : public Decl, public DeclContext {
                                                   GlobalDeclID ID);
 
   CXXExpansionStmtPattern *getExpansionPattern() { return Pattern; }
-  const CXXExpansionStmtPattern *getExpansionPattern() const {
-    return Pattern;
-  }
+  const CXXExpansionStmtPattern *getExpansionPattern() const { return Pattern; }
   void setExpansionPattern(CXXExpansionStmtPattern *S) { Pattern = S; }
 
   CXXExpansionStmtInstantiation *getInstantiations() { return Instantiations; }
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a16483455cfb9..757f2137c90dc 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5561,9 +5561,7 @@ class CXXExpansionSelectExpr : public Expr {
   CXXExpansionSelectExpr(EmptyShell Empty);
   CXXExpansionSelectExpr(const ASTContext &C, InitListExpr *Range, Expr *Idx);
 
-  InitListExpr *getRangeExpr() {
-    return cast<InitListExpr>(SubExprs[RANGE]);
-  }
+  InitListExpr *getRangeExpr() { return cast<InitListExpr>(SubExprs[RANGE]); }
 
   const InitListExpr *getRangeExpr() const {
     return cast<InitListExpr>(SubExprs[RANGE]);
diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index cf44268c6e695..a8371cceb784c 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -760,7 +760,7 @@ class CXXExpansionStmtPattern final
   static CXXExpansionStmtPattern *
   CreateIterating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
                   DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin,
-                  DeclStmt* Iter, SourceLocation LParenLoc,
+                  DeclStmt *Iter, SourceLocation LParenLoc,
                   SourceLocation ColonLoc, SourceLocation RParenLoc);
 
   SourceLocation getLParenLoc() const { return LParenLoc; }
@@ -887,7 +887,6 @@ class CXXExpansionStmtPattern final
     return cast<VarDecl>(getIterVarStmt()->getSingleDecl());
   }
 
-
   // Accessors for destructuring statements.
   Stmt *getDecompositionDeclStmt() {
     assert(isDestructuring());
@@ -1063,7 +1062,9 @@ class CXXExpansionStmtInstantiation final
     return getTrailingObjects(getNumSubStmts());
   }
 
-  unsigned getNumSubStmts() const { return NumInstantiations + NumPreambleStmts; }
+  unsigned getNumSubStmts() const {
+    return NumInstantiations + NumPreambleStmts;
+  }
 
   ArrayRef<Stmt *> getInstantiations() const {
     return getTrailingObjects(NumInstantiations);
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 2cdb4d59426b4..892715a6dac26 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1752,7 +1752,7 @@ class Parser : public CodeCompletionHandler {
     SourceLocation ColonLoc;
     ExprResult RangeExpr;
     SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
-    CXXExpansionStmtDecl* ExpansionStmt = nullptr;
+    CXXExpansionStmtDecl *ExpansionStmt = nullptr;
     bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
   };
   struct ForRangeInfo : ForRangeInit {
diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h
index 06d7a883530cd..fcf3a70ef146d 100644
--- a/clang/include/clang/Sema/Scope.h
+++ b/clang/include/clang/Sema/Scope.h
@@ -313,9 +313,7 @@ class Scope {
     IsExpansionStmtScope = Value;
   }
 
-  bool isExpansionStmtScope() const {
-    return IsExpansionStmtScope;
-  }
+  bool isExpansionStmtScope() const { return IsExpansionStmtScope; }
 
   /// getBreakParent - Return the closest scope that a break statement
   /// would be affected by.
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 464ab01d79e95..d743717ad49de 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -7556,8 +7556,8 @@ ExpectedStmt ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
       ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc);
 }
 
-ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtPattern(
-    CXXExpansionStmtPattern *S) {
+ExpectedStmt
+ASTNodeImporter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
   Error Err = Error::success();
   auto ToESD = importChecked(Err, S->getDecl());
   auto ToInit = importChecked(Err, S->getInit());
@@ -9526,8 +9526,8 @@ ASTNodeImporter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
                                       ToInitLoc, ToBeginLoc, ToEndLoc);
 }
 
-ExpectedStmt ASTNodeImporter::VisitCXXExpansionSelectExpr(
-    CXXExpansionSelectExpr *E) {
+ExpectedStmt
+ASTNodeImporter::VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *E) {
   Error Err = Error::success();
   auto ToRange = importChecked(Err, E->getRangeExpr());
   auto ToIndex = importChecked(Err, E->getIndexExpr());
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 5c334617c6aa7..8af918f58c06d 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -252,6 +252,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
   bool visitCXXTryStmt(const CXXTryStmt *S);
   bool
   visitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *S);
+
 protected:
   bool visitStmt(const Stmt *S);
   bool visitExpr(const Expr *E, bool DestroyToplevelScope) override;
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index b7d6c3d18acd5..a1ea91994969c 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -2035,8 +2035,8 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
 CXXExpansionSelectExpr::CXXExpansionSelectExpr(EmptyShell Empty)
     : Expr(CXXExpansionSelectExprClass, Empty) {}
 
-CXXExpansionSelectExpr::CXXExpansionSelectExpr(
-    const ASTContext &C, InitListExpr *Range, Expr *Idx)
+CXXExpansionSelectExpr::CXXExpansionSelectExpr(const ASTContext &C,
+                                               InitListExpr *Range, Expr *Idx)
     : Expr(CXXExpansionSelectExprClass, C.DependentTy, VK_PRValue,
            OK_Ordinary) {
   setDependence(ExprDependence::TypeValueInstantiation);
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index 3ac68ca04a5ef..ccd542399761e 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -193,8 +193,8 @@ CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateEnumerating(
 
 CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateIterating(
     ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
-    DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin,
-    DeclStmt *Iter, SourceLocation LParenLoc, SourceLocation ColonLoc,
+    DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin, DeclStmt *Iter,
+    SourceLocation LParenLoc, SourceLocation ColonLoc,
     SourceLocation RParenLoc) {
   CXXExpansionStmtPattern *Pattern =
       AllocateAndConstruct(Context, ExpansionStmtKind::Iterating, ESD, Init,
@@ -209,8 +209,7 @@ SourceLocation CXXExpansionStmtPattern::getBeginLoc() const {
   return ParentDecl->getLocation();
 }
 
-DecompositionDecl *
-CXXExpansionStmtPattern::getDecompositionDecl() {
+DecompositionDecl *CXXExpansionStmtPattern::getDecompositionDecl() {
   assert(isDestructuring());
   return cast<DecompositionDecl>(
       cast<DeclStmt>(getDecompositionDeclStmt())->getSingleDecl());
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index d6ed69ede4cd9..9a97d52fd3020 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -475,8 +475,7 @@ void StmtPrinter::VisitCXXExpansionStmtInstantiation(
   llvm_unreachable("should never be printed");
 }
 
-void StmtPrinter::VisitCXXExpansionSelectExpr(
-    CXXExpansionSelectExpr *Node) {
+void StmtPrinter::VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *Node) {
   PrintExpr(Node->getRangeExpr());
 }
 
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index b06022bfd73bb..e3fddf04e555b 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -31,9 +31,9 @@
 #include "clang/Sema/SemaCodeCompletion.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/SemaOpenMP.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/ScopeExit.h"
 #include <optional>
 
 using namespace clang;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 1333512fb2046..df01c91c065a1 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1968,10 +1968,9 @@ void Parser::ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
       std::move(Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
 }
 
-StmtResult
-Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
-                          LabelDecl *PrecedingLabel,
-                          CXXExpansionStmtDecl *ESD) {
+StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
+                                     LabelDecl *PrecedingLabel,
+                                     CXXExpansionStmtDecl *ESD) {
   assert(Tok.is(tok::kw_for) && "Not a for stmt!");
   SourceLocation ForLoc = ConsumeToken();  // eat the 'for'.
 
@@ -2144,7 +2143,7 @@ Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
   } else {
     // An expression here should not be inside the expansion statement context.
     ExpansionStmtContextRAII EnterParentContext{
-      Actions, ForRangeInfo, Actions.CurContext->getParent()};
+        Actions, ForRangeInfo, Actions.CurContext->getParent()};
     ProhibitAttributes(attrs);
     Value = ParseExpression();
 
@@ -2182,8 +2181,7 @@ Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
       // User tried to write the reasonable, but ill-formed, for-range-statement
       //   for (expr : expr) { ... }
       Diag(Tok, diag::err_for_range_expected_decl)
-          << (ESD != nullptr)
-          << FirstPart.get()->getSourceRange();
+          << (ESD != nullptr) << FirstPart.get()->getSourceRange();
       SkipUntil(tok::r_paren, StopBeforeMatch);
       SecondPart = Sema::ConditionError();
     } else {
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index b95612d08fb10..9d8893dbdb731 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -289,8 +289,8 @@ Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
 
   auto *TParam = NonTypeTemplateParmDecl::Create(
       Context, Context.getTranslationUnitDecl(), TemplateKWLoc, TemplateKWLoc,
-      TemplateDepth, /*Position=*/0, /*Id=*/nullptr, ParmTy, /*ParameterPack=*/false,
-      ParmTI);
+      TemplateDepth, /*Position=*/0, /*Id=*/nullptr, ParmTy,
+      /*ParameterPack=*/false, ParmTI);
 
   return BuildCXXExpansionStmtDecl(CurContext, TemplateKWLoc, TParam);
 }
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index a1da5e54d4980..e6afcd4404501 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2464,8 +2464,7 @@ StmtResult Sema::BuildCXXForRangeRangeVar(Scope *S, Expr *Range, QualType Type,
   IdentifierInfo *Name =
       PP.getIdentifierInfo(std::string("__range") + DepthStr);
   SourceLocation RangeLoc = Range->getBeginLoc();
-  VarDecl *RangeVar = BuildForRangeVarDecl(
-      RangeLoc, Type, Name, IsConstexpr);
+  VarDecl *RangeVar = BuildForRangeVarDecl(RangeLoc, Type, Name, IsConstexpr);
   if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
                             diag::err_for_range_deduction_failure))
 
@@ -2769,10 +2768,10 @@ Sema::ForRangeBeginEndInfo Sema::BuildCXXForRangeBeginEndVars(
     BeginName = PP.getIdentifierInfo(std::string("__begin") + DepthStr);
   if (!EndName)
     EndName = PP.getIdentifierInfo(std::string("__end") + DepthStr);
-  VarDecl *BeginVar = BuildForRangeVarDecl(
-      ColonLoc, AutoType, BeginName, IsConstexpr);
-  VarDecl *EndVar = BuildForRangeVarDecl(
-      ColonLoc, AutoType, EndName, IsConstexpr);
+  VarDecl *BeginVar =
+      BuildForRangeVarDecl(ColonLoc, AutoType, BeginName, IsConstexpr);
+  VarDecl *EndVar =
+      BuildForRangeVarDecl(ColonLoc, AutoType, EndName, IsConstexpr);
 
   // Build begin-expr and end-expr and attach to __begin and __end variables.
   ExprResult BeginExpr, EndExpr;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index aa6c6000f6b2b..1dd7be50c9ff2 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9403,7 +9403,7 @@ StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
   // This is required because some parts of an expansion statement (e.g. the
   // init-statement) are not in a dependent context and must thus be transformed
   // in the parent context.
-  auto TransformStmtInParentContext = [&] (Stmt *SubStmt) -> StmtResult {
+  auto TransformStmtInParentContext = [&](Stmt *SubStmt) -> StmtResult {
     Sema::ContextRAII CtxGuard(SemaRef, SemaRef.CurContext->getParent(),
                                /*NewThis=*/false);
     return getDerived().TransformStmt(SubStmt);
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 1cf9c918bc815..87cec16a76323 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1803,8 +1803,7 @@ void ASTStmtReader::VisitCXXExpansionStmtInstantiation(
   S->setShouldApplyLifetimeExtensionToPreamble(Record.readBool());
 }
 
-void ASTStmtReader::VisitCXXExpansionSelectExpr(
-    CXXExpansionSelectExpr *E) {
+void ASTStmtReader::VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *E) {
   VisitExpr(E);
   E->setRangeExpr(cast<InitListExpr>(Record.readSubExpr()));
   E->setIndexExpr(Record.readSubExpr());
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index b04e2db048440..70477f4cf4001 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1764,7 +1764,7 @@ void ASTStmtWriter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
   Record.AddSourceLocation(S->getColonLoc());
   Record.AddSourceLocation(S->getRParenLoc());
   Record.AddDeclRef(S->getDecl());
-  for (Stmt* SubStmt : S->children())
+  for (Stmt *SubStmt : S->children())
     Record.AddStmt(SubStmt);
   Code = serialization::STMT_CXX_EXPANSION_PATTERN;
 }
@@ -1781,8 +1781,7 @@ void ASTStmtWriter::VisitCXXExpansionStmtInstantiation(
   Code = serialization::STMT_CXX_EXPANSION_INSTANTIATION;
 }
 
-void ASTStmtWriter::VisitCXXExpansionSelectExpr(
-    CXXExpansionSelectExpr *E) {
+void ASTStmtWriter::VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *E) {
   VisitExpr(E);
   Record.AddStmt(E->getRangeExpr());
   Record.AddStmt(E->getIndexExpr());

>From 09f6be7736270dd071248b8859166329aa4d4198 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 10 Jul 2026 22:49:04 +0200
Subject: [PATCH 5/5] Move implementation into .cpp file

---
 clang/include/clang/AST/StmtCXX.h | 9 ++-------
 clang/lib/AST/StmtCXX.cpp         | 8 ++++++++
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index a8371cceb784c..60cfd3ee85d4d 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -1082,13 +1082,8 @@ class CXXExpansionStmtInstantiation final
     ShouldApplyLifetimeExtensionToPreamble = Apply;
   }
 
-  SourceLocation getBeginLoc() const {
-    return Parent->getExpansionPattern()->getBeginLoc();
-  }
-
-  SourceLocation getEndLoc() const {
-    return Parent->getExpansionPattern()->getEndLoc();
-  }
+  SourceLocation getBeginLoc() const;
+  SourceLocation getEndLoc() const;
 
   CXXExpansionStmtDecl *getParent() { return Parent; }
   const CXXExpansionStmtDecl *getParent() const { return Parent; }
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index ccd542399761e..704d0d4c6b0df 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -280,3 +280,11 @@ CXXExpansionStmtInstantiation::CreateEmpty(ASTContext &C, EmptyShell Empty,
   return new (Mem)
       CXXExpansionStmtInstantiation(Empty, NumInstantiations, NumPreambleStmts);
 }
+
+SourceLocation CXXExpansionStmtInstantiation::getBeginLoc() const {
+  return Parent->getExpansionPattern()->getBeginLoc();
+}
+
+SourceLocation CXXExpansionStmtInstantiation::getEndLoc() const {
+  return Parent->getExpansionPattern()->getEndLoc();
+}



More information about the cfe-commits mailing list