Skip to content

Commit 07d55ff

Browse files
committed
[Clang] [C++26] Expansion Statements (Part 6)
1 parent 2731a81 commit 07d55ff

File tree

4 files changed

+162
-24
lines changed

4 files changed

+162
-24
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3712,6 +3712,8 @@ def err_conflicting_codeseg_attribute : Error<
37123712
def warn_duplicate_codeseg_attribute : Warning<
37133713
"duplicate code segment specifiers">, InGroup<Section>;
37143714

3715+
def err_expansion_stmt_invalid_init : Error<
3716+
"cannot expand expression of type %0">;
37153717
def err_expansion_stmt_vla : Error<
37163718
"cannot expand variable length array type %0">;
37173719
def err_expansion_stmt_incomplete : Error<

clang/include/clang/Sema/Sema.h

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

1571415714
ExprResult BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx);
1571515715

15716+
ExprResult BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD,
15717+
Expr *Idx);
15718+
1571615719
std::optional<uint64_t>
1571715720
ComputeExpansionSize(CXXExpansionStmtPattern *Expansion);
1571815721
///@}

clang/lib/Sema/SemaExpand.cpp

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,52 @@ TryBuildIterableExpansionStmtInitializer(Sema &S, Expr *ExpansionInitializer,
220220
return Data;
221221
}
222222

223+
static StmtResult BuildDestructuringCXXExpansionStmt(
224+
Sema &S, Expr *ExpansionInitializer, SourceLocation ColonLoc,
225+
bool VarIsConstexpr,
226+
ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
227+
auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
228+
if (VarIsConstexpr)
229+
Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
230+
EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx);
231+
232+
// The declarations should be attached to the parent decl context.
233+
Sema::ContextRAII CtxGuard(
234+
S, S.CurContext->getEnclosingNonExpansionStatementContext(),
235+
/*NewThis=*/false);
236+
237+
UnsignedOrNone Arity =
238+
S.GetDecompositionElementCount(ExpansionInitializer->getType(), ColonLoc);
239+
240+
if (!Arity) {
241+
S.Diag(ExpansionInitializer->getBeginLoc(),
242+
diag::err_expansion_stmt_invalid_init)
243+
<< ExpansionInitializer->getType()
244+
<< ExpansionInitializer->getSourceRange();
245+
return StmtError();
246+
}
247+
248+
QualType AutoRRef = S.Context.getAutoRRefDeductType();
249+
SmallVector<BindingDecl *> Bindings;
250+
for (unsigned I = 0; I < *Arity; ++I)
251+
Bindings.push_back(BindingDecl::Create(
252+
S.Context, S.CurContext, ColonLoc,
253+
S.getPreprocessor().getIdentifierInfo("__u" + std::to_string(I)),
254+
AutoRRef));
255+
256+
TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(AutoRRef);
257+
auto *DD =
258+
DecompositionDecl::Create(S.Context, S.CurContext, ColonLoc, ColonLoc,
259+
AutoRRef, TSI, SC_Auto, Bindings);
260+
261+
if (VarIsConstexpr)
262+
DD->setConstexpr(true);
263+
264+
S.ApplyForRangeOrExpansionStatementLifetimeExtension(DD, LifetimeExtendTemps);
265+
S.AddInitializerToDecl(DD, ExpansionInitializer, false);
266+
return S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(DD), ColonLoc, ColonLoc);
267+
}
268+
223269
CXXExpansionStmtDecl *
224270
Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
225271
SourceLocation TemplateKWLoc) {
@@ -368,8 +414,31 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
368414
Data.EndDecl, LParenLoc, ColonLoc, RParenLoc);
369415
}
370416

371-
Diag(ESD->getLocation(), diag::err_expansion_statements_todo);
372-
return StmtError();
417+
// If not, try destructuring.
418+
StmtResult DecompDeclStmt = BuildDestructuringCXXExpansionStmt(
419+
*this, ExpansionInitializer, ColonLoc, ExpansionVar->isConstexpr(),
420+
LifetimeExtendTemps);
421+
if (DecompDeclStmt.isInvalid()) {
422+
ActOnInitializerError(ExpansionVar);
423+
return StmtError();
424+
}
425+
426+
auto *DS = DecompDeclStmt.getAs<DeclStmt>();
427+
auto *DD = cast<DecompositionDecl>(DS->getSingleDecl());
428+
if (DD->isInvalidDecl())
429+
return StmtError();
430+
431+
ExprResult Select = BuildCXXDestructuringExpansionSelectExpr(DD, Index);
432+
if (Select.isInvalid()) {
433+
ActOnInitializerError(ExpansionVar);
434+
return StmtError();
435+
}
436+
437+
if (FinaliseExpansionVar(*this, ExpansionVar, Select))
438+
return StmtError();
439+
440+
return new (Context) CXXDestructuringExpansionStmtPattern(
441+
ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc);
373442
}
374443

375444
StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
@@ -409,8 +478,8 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
409478
Shared.push_back(Expansion->getRangeVarStmt());
410479
Shared.push_back(Expansion->getBeginVarStmt());
411480
Shared.push_back(Expansion->getEndVarStmt());
412-
} else {
413-
assert(Expansion->isEnumerating() && "TODO");
481+
} else if (Expansion->isDestructuring()) {
482+
Shared.push_back(Expansion->getDecompositionDeclStmt());
414483
}
415484

416485
// Return an empty statement if the range is empty.
@@ -471,6 +540,23 @@ ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) {
471540
return Range->getInit(I);
472541
}
473542

543+
ExprResult Sema::BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD,
544+
Expr *Idx) {
545+
if (Idx->isValueDependent())
546+
return new (Context) CXXDestructuringExpansionSelectExpr(Context, DD, Idx);
547+
548+
Expr::EvalResult ER;
549+
if (!Idx->EvaluateAsInt(ER, Context))
550+
llvm_unreachable("Failed to evaluate expansion index");
551+
552+
uint64_t I = ER.Val.getInt().getZExtValue();
553+
MarkAnyDeclReferenced(Idx->getBeginLoc(), DD, true);
554+
if (auto *BD = DD->bindings()[I]; auto *HVD = BD->getHoldingVar())
555+
return HVD->getInit();
556+
else
557+
return BD->getBinding();
558+
}
559+
474560
std::optional<uint64_t>
475561
Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
476562
assert(!HasDependentSize(Expansion));
@@ -663,5 +749,9 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
663749
return ER.Val.getInt().getZExtValue();
664750
}
665751

666-
llvm_unreachable("TODO");
752+
if (auto *Destructuring =
753+
dyn_cast<CXXDestructuringExpansionStmtPattern>(Expansion))
754+
return Destructuring->getDecompositionDecl()->bindings().size();
755+
756+
llvm_unreachable("Invalid expansion statement class");
667757
}

clang/lib/Sema/TreeTransform.h

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9297,28 +9297,51 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
92979297
template <typename Derived>
92989298
StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
92999299
CXXExpansionStmtPattern *S) {
9300+
ExprResult ExpansionInitializer;
9301+
SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
93009302
CXXExpansionStmtDecl *NewESD = nullptr;
93019303
Stmt *Init = nullptr;
93029304
DeclStmt *ExpansionVarStmt = nullptr;
9303-
Decl *ESD =
9304-
getDerived().TransformDecl(S->getDecl()->getLocation(), S->getDecl());
9305-
if (!ESD || ESD->isInvalidDecl())
9306-
return StmtError();
9307-
NewESD = cast<CXXExpansionStmtDecl>(ESD);
93089305

9309-
Init = S->getInit();
9310-
if (Init) {
9311-
StmtResult SR = getDerived().TransformStmt(Init);
9312-
if (SR.isInvalid())
9306+
// Collect lifetime-extended temporaries in case this ends up being a
9307+
// destructuring expansion statement (for other kinds of expansion statements,
9308+
// this should make no difference since we ignore 'LifetimeExtendTemps' for
9309+
// those).
9310+
{
9311+
EnterExpressionEvaluationContext ExprEvalCtx(
9312+
SemaRef, SemaRef.currentEvaluationContext().Context);
9313+
SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
9314+
SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true;
9315+
Decl *ESD =
9316+
getDerived().TransformDecl(S->getDecl()->getLocation(), S->getDecl());
9317+
if (!ESD || ESD->isInvalidDecl())
93139318
return StmtError();
9314-
Init = SR.get();
9315-
}
9319+
NewESD = cast<CXXExpansionStmtDecl>(ESD);
93169320

9317-
StmtResult ExpansionVar =
9318-
getDerived().TransformStmt(S->getExpansionVarStmt());
9319-
if (ExpansionVar.isInvalid())
9320-
return StmtError();
9321-
ExpansionVarStmt = cast<DeclStmt>(ExpansionVar.get());
9321+
Init = S->getInit();
9322+
if (Init) {
9323+
StmtResult SR = getDerived().TransformStmt(Init);
9324+
if (SR.isInvalid())
9325+
return StmtError();
9326+
Init = SR.get();
9327+
}
9328+
9329+
StmtResult ExpansionVar =
9330+
getDerived().TransformStmt(S->getExpansionVarStmt());
9331+
if (ExpansionVar.isInvalid())
9332+
return StmtError();
9333+
ExpansionVarStmt = cast<DeclStmt>(ExpansionVar.get());
9334+
9335+
if (S->isDependent()) {
9336+
ExpansionInitializer =
9337+
getDerived().TransformExpr(S->getExpansionInitializer());
9338+
if (ExpansionInitializer.isInvalid())
9339+
return StmtError();
9340+
9341+
LifetimeExtendTemps =
9342+
SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps;
9343+
}
9344+
}
93229345

93239346
CXXExpansionStmtPattern *NewPattern = nullptr;
93249347
if (S->isEnumerating()) {
@@ -9355,7 +9378,12 @@ StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
93559378

93569379
NewPattern = cast<CXXExpansionStmtPattern>(Res.get());
93579380
} else {
9358-
llvm_unreachable("TODO");
9381+
// The only time we instantiate an expansion statement is if its expansion
9382+
// size is dependent (otherwise, we only instantiate the expansions and
9383+
// leave the underlying CXXExpansionStmtPattern as-is). Since destructuring
9384+
// expansion statements never have a dependent size, we should never get
9385+
// here.
9386+
llvm_unreachable("destructuring pattern should never be instantiated");
93599387
}
93609388

93619389
StmtResult Body = getDerived().TransformStmt(S->getBody());
@@ -9386,8 +9414,23 @@ StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
93869414
SmallVector<Stmt *> SharedStmts;
93879415
SmallVector<Stmt *> Instantiations;
93889416

9389-
if (TransformStmts(SharedStmts, S->getSharedStmts()))
9390-
return StmtError();
9417+
// Apply lifetime extension to the shared statements if this was a
9418+
// destructuring expansion statement.
9419+
{
9420+
EnterExpressionEvaluationContext ExprEvalCtx(
9421+
SemaRef, SemaRef.currentEvaluationContext().Context);
9422+
SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
9423+
SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true;
9424+
if (TransformStmts(SharedStmts, S->getSharedStmts()))
9425+
return StmtError();
9426+
9427+
if (S->shouldApplyLifetimeExtensionToSharedStmts()) {
9428+
auto *VD =
9429+
cast<VarDecl>(cast<DeclStmt>(SharedStmts.front())->getSingleDecl());
9430+
SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension(
9431+
VD, SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps);
9432+
}
9433+
}
93919434

93929435
if (TransformStmts(Instantiations, S->getInstantiations()))
93939436
return StmtError();

0 commit comments

Comments
 (0)