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

>From b988a4c7ce6ce4ef00057114e0126c275899c735 Mon Sep 17 00:00:00 2001
From: Sirraide <[email protected]>
Date: Wed, 26 Nov 2025 16:18:02 +0100
Subject: [PATCH] [Clang] [C++26] Expansion Statements (Part 5)

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  11 +
 clang/include/clang/Sema/Sema.h               |   6 +
 clang/lib/Sema/SemaExpand.cpp                 | 331 +++++++++++++++++-
 clang/lib/Sema/SemaStmt.cpp                   |   8 +
 clang/lib/Sema/TreeTransform.h                |  74 ++++
 5 files changed, 413 insertions(+), 17 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 598196cd185a3..598bfad6254b0 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<
@@ -3734,6 +3738,13 @@ def err_conflicting_codeseg_attribute : Error<
 def warn_duplicate_codeseg_attribute : Warning<
   "duplicate code segment specifiers">, InGroup<Section>;
 
+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_attribute_patchable_function_entry_invalid_section
     : Error<"section argument to 'patchable_function_entry' attribute is not "
             "valid for this target: %0">;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 91df1dff577e5..050da05a2e82b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15881,6 +15881,12 @@ class Sema final : public SemaBase {
                                                      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>
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index 39823c1dcc295..66e4000ace92d 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -20,9 +20,38 @@
 #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) {
@@ -47,7 +76,8 @@ static auto InitListContainsPack(const InitListExpr *ILE) {
                       [](const Expr *E) { return isa<PackExpansionExpr>(E); });
 }
 
-static bool HasDependentSize(const CXXExpansionStmtPattern *Pattern) {
+static bool HasDependentSize(const DeclContext *CurContext,
+                             const CXXExpansionStmtPattern *Pattern) {
   switch (Pattern->getKind()) {
   case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: {
     auto *SelectExpr = cast<CXXExpansionSelectExpr>(
@@ -56,21 +86,163 @@ static bool HasDependentSize(const CXXExpansionStmtPattern 
*Pattern) {
   }
 
   case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
-  case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
+    // 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:
     llvm_unreachable("TODO");
   }
 
   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
+}
+
 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. CWG 3044 requires this type to
-  // be 'ptrdiff_t' for iterating expansion statements, so use that in all
-  // cases.
+  // 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);
@@ -131,8 +303,19 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern(
                                                    ColonLoc, RParenLoc);
   }
 
-  Diag(ESD->getLocation(), diag::err_expansion_statements_todo);
-  return StmtError();
+  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(
@@ -143,6 +326,74 @@ StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern(
       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);
+  }
+
+  Diag(ESD->getLocation(), diag::err_expansion_statements_todo);
+  return StmtError();
+}
+
 StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
   if (!Exp || !Body)
     return StmtError();
@@ -152,9 +403,14 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt 
*Body) {
          "should not rebuild expansion statement after instantiation");
 
   Expansion->setBody(Body);
-  if (HasDependentSize(Expansion))
+  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)
@@ -171,7 +427,12 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt 
*Body) {
   if (Expansion->getInit())
     Preamble.push_back(Expansion->getInit());
 
-  assert(Expansion->isEnumerating() && "TODO");
+  if (Expansion->isIterating()) {
+    Preamble.push_back(Expansion->getRangeVarStmt());
+    Preamble.push_back(Expansion->getBeginVarStmt());
+  } else {
+    assert(Expansion->isEnumerating() && "TODO");
+  }
 
   // Return an empty statement if the range is empty.
   if (*NumInstantiations == 0) {
@@ -182,21 +443,21 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt 
*Body) {
     return Expansion;
   }
 
-  // Create a compound statement binding the expansion variable and body.
-  Stmt *VarAndBody[] = {Expansion->getExpansionVarStmt(), Body};
+  // 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, VarAndBody, FPOptionsOverride(),
+      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) {
-    // 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);
-
     TemplateArgument Arg{Context, llvm::APSInt::get(I),
                          Context.getPointerDiffType()};
     MultiLevelTemplateArgumentList MTArgList(ESD, Arg, true);
@@ -240,5 +501,41 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern 
*Expansion) {
         ->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
+  }
+
   llvm_unreachable("TODO");
 }
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 65e6a64e37ed6..233d6b008e2af 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2436,12 +2436,20 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr 
*E,
 /// Build a variable declaration for a for-range statement.
 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;
 }
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index d3f2c8295f93a..8ec982ccdaeb7 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9397,6 +9397,43 @@ StmtResult 
TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
     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 =
@@ -9407,6 +9444,43 @@ StmtResult 
TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
     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 {
     llvm_unreachable("TODO");
   }

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

Reply via email to