[llvm-branch-commits] [clang] [Clang] [C++26] Expansion Statements (Part 3: Enumerating Expansion Statements) (PR #169682)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Mar 19 07:07:23 PDT 2026


================
@@ -65,13 +104,144 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern(
     Expr *ExpansionInitializer, SourceLocation LParenLoc,
     SourceLocation ColonLoc, SourceLocation RParenLoc,
     ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+  if (!ExpansionInitializer || !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() ||
+      ExpansionInitializer->containsErrors())
+    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 (FinaliseExpansionVar(*this, ExpansionVar, Initializer))
+      return StmtError();
+
+    // Note that lifetime extension only applies to destructuring expansion
+    // statements, so we just ignore 'LifetimeExtendedTemps' entirely for other
+    // types of expansion statements (this is CWG 3043).
+    return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc,
+                                                   ColonLoc, RParenLoc);
+  }
+
   Diag(ESD->getLocation(), diag::err_expansion_statements_todo);
   return StmtError();
 }
 
+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::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(Expansion))
+    return Expansion;
+
+  // This can fail if this is an iterating expansion statement.
+  std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion);
+  if (!NumInstantiations)
+    return StmtError();
+
+  // Collect shared statements.
+  SmallVector<Stmt *, 1> Shared;
+  if (Expansion->getInit())
+    Shared.push_back(Expansion->getInit());
+
+  assert(Expansion->isEnumerating() && "TODO");
+
+  // Return an empty statement if the range is empty.
+  if (*NumInstantiations == 0) {
+    Expansion->getDecl()->setInstantiations(
+        CXXExpansionStmtInstantiation::Create(
+            Context, Expansion->getBeginLoc(), Expansion->getEndLoc(),
+            /*Instantiations=*/{}, Shared, Expansion->isDestructuring()));
+    return Expansion;
+  }
+
+  // Create a compound statement binding the expansion variable and body.
+  Stmt *VarAndBody[] = {Expansion->getExpansionVarStmt(), Body};
+  Stmt *CombinedBody =
+      CompoundStmt::Create(Context, VarAndBody, 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);
+    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->getBeginLoc(), Expansion->getEndLoc(), Instantiations,
+      Shared, Expansion->isDestructuring());
+
+  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.
+  Expr::EvalResult ER;
----------------
Sirraide wrote:

Yes, I forgot that was a thing

https://github.com/llvm/llvm-project/pull/169682


More information about the llvm-branch-commits mailing list