[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
Tue Mar 24 08:29:50 PDT 2026
https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/169682
>From be79edd149978f1b595e498b09ef0c9f79b76a8c Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 25 Nov 2025 20:47:23 +0100
Subject: [PATCH] [Clang] [C++26] Expansion Statements (Part 3)
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +
clang/include/clang/Sema/Sema.h | 20 +++
clang/lib/Frontend/FrontendActions.cpp | 2 +
clang/lib/Sema/SemaExpand.cpp | 164 ++++++++++++++++++
clang/lib/Sema/SemaTemplateInstantiate.cpp | 29 +++-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 38 +++-
clang/lib/Sema/SemaTemplateVariadic.cpp | 8 +-
clang/lib/Sema/TreeTransform.h | 90 +++++++++-
.../Parser/cxx2c-expansion-statements.cpp | 80 ++++-----
clang/test/SemaTemplate/GH176155.cpp | 20 +--
10 files changed, 388 insertions(+), 65 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 50fb1c607b789..0e52becd21779 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5935,6 +5935,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/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 190c0eed2e141..24c81ed9ea590 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13288,6 +13288,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.
@@ -13483,6 +13486,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,
@@ -15803,6 +15812,17 @@ class Sema final : public SemaBase {
ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps);
StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body);
+
+ StmtResult BuildCXXEnumeratingExpansionStmtPattern(Decl *ESD, Stmt *Init,
+ Stmt *ExpansionVar,
+ SourceLocation LParenLoc,
+ SourceLocation ColonLoc,
+ SourceLocation RParenLoc);
+
+ ExprResult BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx);
+
+ std::optional<uint64_t>
+ ComputeExpansionSize(CXXExpansionStmtPattern *Expansion);
///@}
};
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index 7bcb4d7d89db1..39362242940f8 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -480,6 +480,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
return "SYCLKernelLaunchLookup";
case CodeSynthesisContext::SYCLKernelLaunchOverloadResolution:
return "SYCLKernelLaunchOverloadResolution";
+ case CodeSynthesisContext::ExpansionStmtInstantiation:
+ return "ExpansionStmtInstantiation";
}
return "";
}
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index 449ff88d1a3ad..106cc7b608212 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -24,6 +24,45 @@
using namespace clang;
using namespace sema;
+// Build a 'DeclRefExpr' designating the template parameter '__N'.
+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 CXXExpansionStmtPattern *Pattern) {
+ switch (Pattern->getKind()) {
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: {
+ auto *SelectExpr = cast<CXXExpansionSelectExpr>(
+ Pattern->getExpansionVariable()->getInit());
+ return InitListContainsPack(SelectExpr->getRangeExpr());
+ }
+
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent:
+ llvm_unreachable("TODO");
+ }
+
+ llvm_unreachable("invalid pattern kind");
+}
CXXExpansionStmtDecl *
Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
@@ -65,13 +104,138 @@ 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 (FinalizeExpansionVar(*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->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();
+
llvm_unreachable("TODO");
}
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..42c094fc2e94d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9372,19 +9372,101 @@ 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();
+ }
+
+ 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 {
+ llvm_unreachable("TODO");
+ }
+
+ 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;
+ };
+
+ SmallVector<Stmt *> SharedStmts;
+ SmallVector<Stmt *> Instantiations;
+
+ if (TransformStmts(SharedStmts, S->getSharedStmts()))
+ return StmtError();
+
+ if (TransformStmts(Instantiations, S->getInstantiations()))
+ return StmtError();
+
+ if (!getDerived().AlwaysRebuild() && !SubStmtChanged)
+ return S;
+
+ return CXXExpansionStmtInstantiation::Create(
+ SemaRef.Context, S->getBeginLoc(), S->getEndLoc(), Instantiations,
+ SharedStmts, S->shouldApplyLifetimeExtensionToSharedStmts());
}
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 +15908,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/Parser/cxx2c-expansion-statements.cpp b/clang/test/Parser/cxx2c-expansion-statements.cpp
index 658257922b734..e58b9a189041c 100644
--- a/clang/test/Parser/cxx2c-expansion-statements.cpp
+++ b/clang/test/Parser/cxx2c-expansion-statements.cpp
@@ -10,56 +10,56 @@ struct initializer_list {
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 be a range-based for loop}} expected-error {{TODO (expansion statements)}}
- template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}}
- template for (;;); // expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}}
- template for (int x;;); // expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}}
- template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}} expected-error {{TODO (expansion statements)}}
- template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}}
- template for (auto y : {1})]; // expected-error {{expected expression}} expected-error {{TODO (expansion statements)}}
- template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{TODO (expansion statements)}}
- template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}} expected-error {{TODO (expansion statements)}}
- 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'}} expected-error {{TODO (expansion statements)}}
- template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} expected-error {{TODO (expansion statements)}}
- template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}} expected-error {{TODO (expansion statements)}}
- template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}} expected-error {{TODO (expansion statements)}}
- template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}} expected-error {{TODO (expansion statements)}}
- template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} expected-error {{TODO (expansion statements)}}
- template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}} expected-error {{TODO (expansion statements)}}
- template for (consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}} expected-error {{TODO (expansion statements)}}
- template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}} expected-error {{TODO (expansion statements)}}
- 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'}} expected-error {{TODO (expansion statements)}}
- template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} expected-error {{TODO (expansion statements)}}
- template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}} expected-error {{TODO (expansion statements)}}
- template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}} expected-error {{TODO (expansion statements)}}
- template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}} expected-error {{TODO (expansion statements)}}
- template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} expected-error {{TODO (expansion statements)}}
- template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}} expected-error {{TODO (expansion statements)}}
- template for (int x; consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}} expected-error {{TODO (expansion statements)}}
- template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}} expected-error {{TODO (expansion statements)}}
+ template for (); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}}
+ template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}}
+ template for (;;); // expected-error {{expansion statement must be a range-based for loop}}
+ template for (int x;;); // expected-error {{expansion statement must be 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 be 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 (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 be a range-based for loop}} expected-error {{TODO (expansion statements)}}
+ expected-error {{expansion statement must be a range-based for loop}}
;
template while (true) {} // expected-error {{'template while' is invalid}}
template do {} while (true); // expected-error {{'template do' is invalid}}
- for template (int x : {}) {} // expected-error {{'for template' is invalid; use 'template for' instead}} expected-error {{TODO (expansion statements)}}
+ for template (int x : {}) {} // expected-error {{'for template' is invalid; use 'template for' instead}}
}
void good() {
- template for (auto y : {}); // expected-error {{TODO (expansion statements)}}
- template for (auto y : {1, 2}); // expected-error {{TODO (expansion statements)}}
- template for (int x; auto y : {1, 2}); // expected-error {{TODO (expansion statements)}}
- template for (int x; int y : {1, 2}); // expected-error {{TODO (expansion statements)}}
- template for (int x; constexpr auto y : {1, 2}); // expected-error {{TODO (expansion statements)}}
- template for (int x; constexpr int y : {1, 2}); // expected-error {{TODO (expansion statements)}}
- template for (constexpr int a : {1, 2}) { // expected-error {{TODO (expansion statements)}}
- template for (constexpr int b : {1, 2}) { // expected-error {{TODO (expansion statements)}}
- template for (constexpr int c : {1, 2}); // expected-error {{TODO (expansion statements)}}
+ 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,}) {} // expected-error {{TODO (expansion statements)}}
- template for (int x : {,}) {} // expected-error {{expected expression}} expected-error {{TODO (expansion statements)}}
+ template for (int x : {1, 2,}) {}
+ template for (int x : {,}) {} // expected-error {{expected expression}}
}
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); }
More information about the llvm-branch-commits
mailing list