[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