Skip to content

Commit 56ad658

Browse files
committed
[Clang] [C++26] Expansion Statements (Part 3)
1 parent f92568a commit 56ad658

File tree

9 files changed

+391
-51
lines changed

9 files changed

+391
-51
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5827,6 +5827,8 @@ def note_template_nsdmi_here : Note<
58275827
"in instantiation of default member initializer %q0 requested here">;
58285828
def note_template_type_alias_instantiation_here : Note<
58295829
"in instantiation of template type alias %0 requested here">;
5830+
def note_expansion_stmt_instantiation_here : Note<
5831+
"in instantiation of expansion statement requested here">;
58305832
def note_template_exception_spec_instantiation_here : Note<
58315833
"in instantiation of exception specification for %0 requested here">;
58325834
def note_template_requirement_instantiation_here : Note<

clang/include/clang/Sema/Sema.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13176,6 +13176,9 @@ class Sema final : public SemaBase {
1317613176

1317713177
/// We are performing partial ordering for template template parameters.
1317813178
PartialOrderingTTP,
13179+
13180+
/// We are instantiating an expansion statement.
13181+
ExpansionStmtInstantiation,
1317913182
} Kind;
1318013183

1318113184
/// Whether we're substituting into constraints.
@@ -13371,6 +13374,12 @@ class Sema final : public SemaBase {
1337113374
concepts::Requirement *Req,
1337213375
SourceRange InstantiationRange = SourceRange());
1337313376

13377+
/// \brief Note that we are substituting the body of an expansion statement.
13378+
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
13379+
CXXExpansionStmtPattern *ExpansionStmt,
13380+
ArrayRef<TemplateArgument> TArgs,
13381+
SourceRange InstantiationRange);
13382+
1337413383
/// \brief Note that we are checking the satisfaction of the constraint
1337513384
/// expression inside of a nested requirement.
1337613385
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
@@ -15643,6 +15652,19 @@ class Sema final : public SemaBase {
1564315652
ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps);
1564415653

1564515654
StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body);
15655+
15656+
StmtResult BuildCXXEnumeratingExpansionStmtPattern(Decl *ESD, Stmt *Init,
15657+
Stmt *ExpansionVar,
15658+
SourceLocation LParenLoc,
15659+
SourceLocation ColonLoc,
15660+
SourceLocation RParenLoc);
15661+
15662+
ExprResult
15663+
BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range,
15664+
Expr *Idx);
15665+
15666+
std::optional<uint64_t>
15667+
ComputeExpansionSize(CXXExpansionStmtPattern *Expansion);
1564615668
///@}
1564715669
};
1564815670

clang/lib/Frontend/FrontendActions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
476476
return "TypeAliasTemplateInstantiation";
477477
case CodeSynthesisContext::PartialOrderingTTP:
478478
return "PartialOrderingTTP";
479+
case CodeSynthesisContext::ExpansionStmtInstantiation:
480+
return "ExpansionStmtInstantiation";
479481
}
480482
return "";
481483
}

clang/lib/Sema/SemaExpand.cpp

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@
2424
using namespace clang;
2525
using namespace sema;
2626

27+
// Build a 'DeclRefExpr' designating the template parameter '__N'.
28+
static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) {
29+
return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(),
30+
S.Context.getPointerDiffType(), VK_PRValue,
31+
ESD->getBeginLoc());
32+
}
33+
34+
static bool FinaliseExpansionVar(Sema &S, VarDecl *ExpansionVar,
35+
ExprResult Initializer) {
36+
if (Initializer.isInvalid()) {
37+
S.ActOnInitializerError(ExpansionVar);
38+
return true;
39+
}
40+
41+
S.AddInitializerToDecl(ExpansionVar, Initializer.get(), /*DirectInit=*/false);
42+
return ExpansionVar->isInvalidDecl();
43+
}
2744

2845
CXXExpansionStmtDecl *
2946
Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
@@ -66,13 +83,147 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern(
6683
Expr *ExpansionInitializer, SourceLocation LParenLoc,
6784
SourceLocation ColonLoc, SourceLocation RParenLoc,
6885
ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
86+
if (!ExpansionInitializer || !ExpansionVarStmt)
87+
return StmtError();
88+
89+
assert(CurContext->isExpansionStmt());
90+
auto *DS = cast<DeclStmt>(ExpansionVarStmt);
91+
if (!DS->isSingleDecl()) {
92+
Diag(DS->getBeginLoc(), diag::err_type_defined_in_for_range);
93+
return StmtError();
94+
}
95+
96+
VarDecl *ExpansionVar = dyn_cast<VarDecl>(DS->getSingleDecl());
97+
if (!ExpansionVar || ExpansionVar->isInvalidDecl() ||
98+
ExpansionInitializer->containsErrors())
99+
return StmtError();
100+
101+
// This is an enumerating expansion statement.
102+
if (auto *ILE = dyn_cast<CXXExpansionInitListExpr>(ExpansionInitializer)) {
103+
ExprResult Initializer =
104+
BuildCXXExpansionInitListSelectExpr(ILE, BuildIndexDRE(*this, ESD));
105+
if (FinaliseExpansionVar(*this, ExpansionVar, Initializer))
106+
return StmtError();
107+
108+
// Note that lifetime extension only applies to destructuring expansion
109+
// statements, so we just ignore 'LifetimeExtendedTemps' entirely for other
110+
// types of expansion statements (this is CWG 3043).
111+
return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc,
112+
ColonLoc, RParenLoc);
113+
}
114+
69115
Diag(ESD->getLocation(), diag::err_expansion_statements_todo);
70116
return StmtError();
71117
}
72118

119+
StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern(
120+
Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc,
121+
SourceLocation ColonLoc, SourceLocation RParenLoc) {
122+
return new (Context) CXXEnumeratingExpansionStmtPattern(
123+
cast<CXXExpansionStmtDecl>(ESD), Init, cast<DeclStmt>(ExpansionVar),
124+
LParenLoc, ColonLoc, RParenLoc);
125+
}
126+
73127
StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
74128
if (!Exp || !Body)
75129
return StmtError();
76130

131+
auto *Expansion = cast<CXXExpansionStmtPattern>(Exp);
132+
assert(!Expansion->getDecl()->getInstantiations() &&
133+
"should not rebuild expansion statement after instantiation");
134+
135+
Expansion->setBody(Body);
136+
if (Expansion->hasDependentSize())
137+
return Expansion;
138+
139+
// This can fail if this is an iterating expansion statement.
140+
std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion);
141+
if (!NumInstantiations)
142+
return StmtError();
143+
144+
// Collect shared statements.
145+
SmallVector<Stmt *, 1> Shared;
146+
if (Expansion->getInit())
147+
Shared.push_back(Expansion->getInit());
148+
149+
assert(isa<CXXEnumeratingExpansionStmtPattern>(Expansion) && "TODO");
150+
151+
// Return an empty statement if the range is empty.
152+
if (*NumInstantiations == 0) {
153+
Expansion->getDecl()->setInstantiations(
154+
CXXExpansionStmtInstantiation::Create(
155+
Context, Expansion->getBeginLoc(), Expansion->getEndLoc(),
156+
/*Instantiations=*/{}, Shared,
157+
isa<CXXDestructuringExpansionStmtPattern>(Expansion)));
158+
return Expansion;
159+
}
160+
161+
// Create a compound statement binding the expansion variable and body.
162+
Stmt *VarAndBody[] = {Expansion->getExpansionVarStmt(), Body};
163+
Stmt *CombinedBody =
164+
CompoundStmt::Create(Context, VarAndBody, FPOptionsOverride(),
165+
Body->getBeginLoc(), Body->getEndLoc());
166+
167+
// Expand the body for each instantiation.
168+
SmallVector<Stmt *, 4> Instantiations;
169+
CXXExpansionStmtDecl *ESD = Expansion->getDecl();
170+
for (uint64_t I = 0; I < *NumInstantiations; ++I) {
171+
// Now that we're expanding this, exit the context of the expansion stmt
172+
// so that we no longer treat this as dependent.
173+
ContextRAII CtxGuard(*this, CurContext->getParent(),
174+
/*NewThis=*/false);
175+
176+
TemplateArgument Arg{Context, llvm::APSInt::get(I),
177+
Context.getPointerDiffType()};
178+
MultiLevelTemplateArgumentList MTArgList(ESD, Arg, true);
179+
MTArgList.addOuterRetainedLevels(
180+
Expansion->getDecl()->getIndexTemplateParm()->getDepth());
181+
182+
LocalInstantiationScope LIScope(*this, /*CombineWithOuterScope=*/true);
183+
NonSFINAEContext _(*this);
184+
InstantiatingTemplate Inst(*this, Body->getBeginLoc(), Expansion, Arg,
185+
Body->getSourceRange());
186+
187+
StmtResult Instantiation = SubstStmt(CombinedBody, MTArgList);
188+
if (Instantiation.isInvalid())
189+
return StmtError();
190+
Instantiations.push_back(Instantiation.get());
191+
}
192+
193+
auto *InstantiationsStmt = CXXExpansionStmtInstantiation::Create(
194+
Context, Expansion->getBeginLoc(), Expansion->getEndLoc(), Instantiations,
195+
Shared, isa<CXXDestructuringExpansionStmtPattern>(Expansion));
196+
197+
Expansion->getDecl()->setInstantiations(InstantiationsStmt);
198+
return Expansion;
199+
}
200+
201+
ExprResult
202+
Sema::BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range,
203+
Expr *Idx) {
204+
if (Range->containsPackExpansion() || Idx->isValueDependent())
205+
return new (Context) CXXExpansionInitListSelectExpr(Context, Range, Idx);
206+
207+
// The index is a DRE to a template parameter; we should never
208+
// fail to evaluate it.
209+
Expr::EvalResult ER;
210+
if (!Idx->EvaluateAsInt(ER, Context))
211+
llvm_unreachable("Failed to evaluate expansion index");
212+
213+
uint64_t I = ER.Val.getInt().getZExtValue();
214+
return Range->getExprs()[I];
215+
}
216+
217+
std::optional<uint64_t>
218+
Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
219+
assert(!Expansion->hasDependentSize());
220+
221+
if (isa<CXXEnumeratingExpansionStmtPattern>(Expansion))
222+
return cast<CXXExpansionInitListSelectExpr>(
223+
Expansion->getExpansionVariable()->getInit())
224+
->getRangeExpr()
225+
->getExprs()
226+
.size();
227+
77228
llvm_unreachable("TODO");
78229
}

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
573573
case PriorTemplateArgumentSubstitution:
574574
case ConstraintsCheck:
575575
case NestedRequirementConstraintsCheck:
576+
case ExpansionStmtInstantiation:
576577
return true;
577578

578579
case RequirementInstantiation:
@@ -759,6 +760,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
759760
PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
760761
/*Template=*/nullptr, /*TemplateArgs=*/{}) {}
761762

763+
Sema::InstantiatingTemplate::InstantiatingTemplate(
764+
Sema &SemaRef, SourceLocation PointOfInstantiation,
765+
CXXExpansionStmtPattern *ExpansionStmt, ArrayRef<TemplateArgument> TArgs,
766+
SourceRange InstantiationRange)
767+
: InstantiatingTemplate(
768+
SemaRef, CodeSynthesisContext::ExpansionStmtInstantiation,
769+
PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
770+
/*Template=*/nullptr, /*TemplateArgs=*/TArgs) {}
771+
762772
Sema::InstantiatingTemplate::InstantiatingTemplate(
763773
Sema &SemaRef, SourceLocation PointOfInstantiation,
764774
concepts::NestedRequirement *Req, ConstraintsCheck,
@@ -1260,6 +1270,9 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) {
12601270
<< /*isTemplateTemplateParam=*/true
12611271
<< Active->InstantiationRange);
12621272
break;
1273+
case CodeSynthesisContext::ExpansionStmtInstantiation:
1274+
Diags.Report(Active->PointOfInstantiation,
1275+
diag::note_expansion_stmt_instantiation_here);
12631276
}
12641277
}
12651278
}
@@ -1894,6 +1907,12 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
18941907
maybeInstantiateFunctionParameterToScope(PVD))
18951908
return nullptr;
18961909

1910+
if (isa<CXXExpansionStmtDecl>(D)) {
1911+
assert(SemaRef.CurrentInstantiationScope);
1912+
return cast<Decl *>(
1913+
*SemaRef.CurrentInstantiationScope->findInstantiationOf(D));
1914+
}
1915+
18971916
return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
18981917
}
18991918

@@ -2288,9 +2307,13 @@ ExprResult
22882307
TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E,
22892308
ValueDecl *PD) {
22902309
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
2291-
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found
2292-
= getSema().CurrentInstantiationScope->findInstantiationOf(PD);
2293-
assert(Found && "no instantiation for parameter pack");
2310+
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found =
2311+
getSema().CurrentInstantiationScope->getInstantiationOfIfExists(PD);
2312+
2313+
// This can happen when instantiating an expansion statement that contains
2314+
// a pack (e.g. `template for (auto x : {{ts...}})`).
2315+
if (!Found)
2316+
return E;
22942317

22952318
Decl *TransformedDecl;
22962319
if (DeclArgumentPack *Pack = dyn_cast<DeclArgumentPack *>(*Found)) {

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2089,7 +2089,37 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
20892089

20902090
Decl *TemplateDeclInstantiator::VisitCXXExpansionStmtDecl(
20912091
CXXExpansionStmtDecl *OldESD) {
2092-
llvm_unreachable("TODO");
2092+
Decl *Index = VisitNonTypeTemplateParmDecl(OldESD->getIndexTemplateParm());
2093+
CXXExpansionStmtDecl *NewESD = SemaRef.BuildCXXExpansionStmtDecl(
2094+
Owner, OldESD->getBeginLoc(), cast<NonTypeTemplateParmDecl>(Index));
2095+
SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldESD, NewESD);
2096+
2097+
// If this was already expanded, only instantiate the expansion and
2098+
// don't touch the unexpanded expansion statement.
2099+
if (CXXExpansionStmtInstantiation *OldInst = OldESD->getInstantiations()) {
2100+
StmtResult NewInst = SemaRef.SubstStmt(OldInst, TemplateArgs);
2101+
if (NewInst.isInvalid())
2102+
return nullptr;
2103+
2104+
NewESD->setInstantiations(NewInst.getAs<CXXExpansionStmtInstantiation>());
2105+
NewESD->setExpansionPattern(OldESD->getExpansionPattern());
2106+
return NewESD;
2107+
}
2108+
2109+
// Enter the scope of this expansion statement; don't do this if we've
2110+
// already expanded it, as in that case we no longer want to treat its
2111+
// content as dependent.
2112+
Sema::ContextRAII Context(SemaRef, NewESD, /*NewThis=*/false);
2113+
2114+
StmtResult Expansion =
2115+
SemaRef.SubstStmt(OldESD->getExpansionPattern(), TemplateArgs);
2116+
if (Expansion.isInvalid())
2117+
return nullptr;
2118+
2119+
// The code that handles CXXExpansionStmtPattern takes care of calling
2120+
// setInstantiation() on the ESD if there was an expansion.
2121+
NewESD->setExpansionPattern(cast<CXXExpansionStmtPattern>(Expansion.get()));
2122+
return NewESD;
20932123
}
20942124

20952125
Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
@@ -7093,6 +7123,12 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
70937123
// anonymous unions in class templates).
70947124
}
70957125

7126+
if (CurrentInstantiationScope) {
7127+
if (auto Found = CurrentInstantiationScope->getInstantiationOfIfExists(D))
7128+
if (auto *FD = dyn_cast<NamedDecl>(cast<Decl *>(*Found)))
7129+
return FD;
7130+
}
7131+
70967132
if (!ParentDependsOnArgs)
70977133
return D;
70987134

clang/lib/Sema/SemaTemplateVariadic.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -912,10 +912,14 @@ bool Sema::CheckParameterPacksForExpansion(
912912
unsigned NewPackSize, PendingPackExpansionSize = 0;
913913
if (IsVarDeclPack) {
914914
// Figure out whether we're instantiating to an argument pack or not.
915+
//
916+
// The instantiation may not exist; this can happen when instantiating an
917+
// expansion statement that contains a pack (e.g.
918+
// `template for (auto x : {{ts...}})`).
915919
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
916-
CurrentInstantiationScope->findInstantiationOf(
920+
CurrentInstantiationScope->getInstantiationOfIfExists(
917921
cast<NamedDecl *>(ParmPack.first));
918-
if (isa<DeclArgumentPack *>(*Instantiation)) {
922+
if (Instantiation && isa<DeclArgumentPack *>(*Instantiation)) {
919923
// We could expand this function parameter pack.
920924
NewPackSize = cast<DeclArgumentPack *>(*Instantiation)->size();
921925
} else {

0 commit comments

Comments
 (0)