[llvm-branch-commits] [clang] [Clang] [C++26] Expansion Statements (Part 5: Iterating Expansion Statements) (PR #169684)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri May 29 11:07:18 PDT 2026
https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/169684
>From 7a310e24ccc08ac7ebf3970d9b862ae822cfc44a Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
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 | 11 +
clang/lib/Sema/SemaExpand.cpp | 317 +++++++++++++++++-
clang/lib/Sema/SemaStmt.cpp | 8 +
clang/lib/Sema/TreeTransform.h | 71 ++++
5 files changed, 404 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 598196cd185a3..6da6d3cf56b35 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 currently not 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 5a7ac72af5df3..8df017f1f197b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11210,6 +11210,11 @@ class Sema final : public SemaBase {
VarDecl *BuildForRangeVarDecl(SourceLocation Loc, QualType Type,
IdentifierInfo *Name, bool Constexpr);
+ /// 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,
+ StringRef Name, bool Constexpr);
+
/// 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,
@@ -15881,6 +15886,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 f1fef24d59479..97a051a34d13f 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -20,9 +20,28 @@
#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 {
+ enum class State {
+ NotIterable,
+ Error,
+ Ok,
+ };
+
+ DeclStmt *RangeDecl = nullptr;
+ DeclStmt *BeginDecl = nullptr;
+ DeclStmt *IterDecl = nullptr;
+ State TheState = State::NotIterable;
+
+ bool isIterable() const { return TheState == State::Ok; }
+ bool hasError() { return TheState == State::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 +66,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,14 +76,154 @@ 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::State::Error;
+ Scope *Scope = S.getCurScope();
+
+ // CWG 3131: 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;
+
+ // CWG 3140: 'range', 'begin', and 'iter' are 'constexpr' iff the
+ // for-range-declaration is declared 'constexpr'.
+ //
+ // FIXME: As of CWG 3140, 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;
+
+ // CWG 3140: At runtime, we only need to evaluate 'begin', whereas 'end' is
+ // only used at compile-time; we'll rebuild it when we compute the expansion
+ // size, so only build '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::State::Ok;
+ return Data;
+#endif
+}
+
CXXExpansionStmtDecl *
Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
SourceLocation TemplateKWLoc) {
@@ -129,12 +289,27 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern(
// 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).
+ //
+ // TODO: CWG 3131 makes it so the 'range' variable of an iterating
+ // expansion statement need no longer be 'constexpr'... so do we want
+ // lifetime extension for iterating expansion statements after all?
return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc,
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(
@@ -145,6 +320,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();
@@ -154,9 +397,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)
@@ -173,7 +421,12 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
if (Expansion->getInit())
Shared.push_back(Expansion->getInit());
- assert(Expansion->isEnumerating() && "TODO");
+ if (Expansion->isIterating()) {
+ Shared.push_back(Expansion->getRangeVarStmt());
+ Shared.push_back(Expansion->getBeginVarStmt());
+ } else {
+ assert(Expansion->isEnumerating() && "TODO");
+ }
// Return an empty statement if the range is empty.
if (*NumInstantiations == 0) {
@@ -184,21 +437,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);
@@ -242,5 +495,41 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
->getRangeExpr()
->getNumInits();
+ // CWG 3131: 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 9793788fb75b8..df12dd837d45e 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 Constexpr) {
+ // Making the variable constexpr doesn't automatically add 'const' to the
+ // type, so do that now.
+ if (Constexpr && !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 (Constexpr)
+ // CWG 3044: Do not make the variable 'static'.
+ Decl->setConstexpr(true);
return Decl;
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 42c094fc2e94d..864667d784e6d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9397,6 +9397,40 @@ 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();
+ }
+
+ LifetimeExtendTemps =
+ SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps;
+ }
+
CXXExpansionStmtPattern *NewPattern = nullptr;
if (S->isEnumerating()) {
StmtResult ExpansionVar =
@@ -9407,6 +9441,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");
}
More information about the llvm-branch-commits
mailing list