https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/169685
>From e3c61804686c4239d6ad53d4170ccb2563dac0cd Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Wed, 26 Nov 2025 17:00:57 +0100 Subject: [PATCH 1/6] [Clang] [C++26] Expansion Statements (Part 6) --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/Sema.h | 3 + clang/lib/Sema/SemaExpand.cpp | 100 +++++++++++++++++- clang/lib/Sema/TreeTransform.h | 81 ++++++++++---- 4 files changed, 162 insertions(+), 24 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3c3589e0ae22a..d91e1526ae47f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3700,6 +3700,8 @@ def err_conflicting_codeseg_attribute : Error< def warn_duplicate_codeseg_attribute : Warning< "duplicate code segment specifiers">, InGroup<Section>; +def err_expansion_stmt_invalid_init : Error< + "cannot expand expression of type %0">; def err_expansion_stmt_vla : Error< "cannot expand variable length array type %0">; def err_expansion_stmt_incomplete : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f5a36626a9dad..48bf7ba1083b7 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -15712,6 +15712,9 @@ class Sema final : public SemaBase { ExprResult BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx); + ExprResult BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD, + Expr *Idx); + std::optional<uint64_t> ComputeExpansionSize(CXXExpansionStmtPattern *Expansion); ///@} diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index 40891e96e97de..315a4bdfd3a6c 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -220,6 +220,52 @@ TryBuildIterableExpansionStmtInitializer(Sema &S, Expr *ExpansionInitializer, return Data; } +static StmtResult BuildDestructuringCXXExpansionStmt( + Sema &S, Expr *ExpansionInitializer, SourceLocation ColonLoc, + bool VarIsConstexpr, + ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) { + 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->getEnclosingNonExpansionStatementContext(), + /*NewThis=*/false); + + UnsignedOrNone Arity = + S.GetDecompositionElementCount(ExpansionInitializer->getType(), ColonLoc); + + if (!Arity) { + S.Diag(ExpansionInitializer->getBeginLoc(), + diag::err_expansion_stmt_invalid_init) + << ExpansionInitializer->getType() + << ExpansionInitializer->getSourceRange(); + return StmtError(); + } + + QualType AutoRRef = S.Context.getAutoRRefDeductType(); + SmallVector<BindingDecl *> Bindings; + for (unsigned I = 0; I < *Arity; ++I) + Bindings.push_back(BindingDecl::Create( + S.Context, S.CurContext, ColonLoc, + S.getPreprocessor().getIdentifierInfo("__u" + std::to_string(I)), + AutoRRef)); + + TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(AutoRRef); + auto *DD = + DecompositionDecl::Create(S.Context, S.CurContext, ColonLoc, ColonLoc, + AutoRRef, TSI, SC_Auto, Bindings); + + if (VarIsConstexpr) + DD->setConstexpr(true); + + S.ApplyForRangeOrExpansionStatementLifetimeExtension(DD, LifetimeExtendTemps); + S.AddInitializerToDecl(DD, ExpansionInitializer, false); + return S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(DD), ColonLoc, ColonLoc); +} + CXXExpansionStmtDecl * Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth, SourceLocation TemplateKWLoc) { @@ -368,8 +414,31 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( Data.EndDecl, LParenLoc, ColonLoc, RParenLoc); } - Diag(ESD->getLocation(), diag::err_expansion_statements_todo); - return StmtError(); + // If not, try destructuring. + StmtResult DecompDeclStmt = BuildDestructuringCXXExpansionStmt( + *this, ExpansionInitializer, ColonLoc, ExpansionVar->isConstexpr(), + LifetimeExtendTemps); + if (DecompDeclStmt.isInvalid()) { + ActOnInitializerError(ExpansionVar); + return StmtError(); + } + + auto *DS = DecompDeclStmt.getAs<DeclStmt>(); + auto *DD = cast<DecompositionDecl>(DS->getSingleDecl()); + if (DD->isInvalidDecl()) + return StmtError(); + + ExprResult Select = BuildCXXDestructuringExpansionSelectExpr(DD, Index); + if (Select.isInvalid()) { + ActOnInitializerError(ExpansionVar); + return StmtError(); + } + + if (FinaliseExpansionVar(*this, ExpansionVar, Select)) + return StmtError(); + + return new (Context) CXXDestructuringExpansionStmtPattern( + ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc); } StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { @@ -409,8 +478,8 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { Shared.push_back(Expansion->getRangeVarStmt()); Shared.push_back(Expansion->getBeginVarStmt()); Shared.push_back(Expansion->getEndVarStmt()); - } else { - assert(Expansion->isEnumerating() && "TODO"); + } else if (Expansion->isDestructuring()) { + Shared.push_back(Expansion->getDecompositionDeclStmt()); } // Return an empty statement if the range is empty. @@ -471,6 +540,23 @@ ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) { return Range->getInit(I); } +ExprResult Sema::BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD, + Expr *Idx) { + if (Idx->isValueDependent()) + return new (Context) CXXDestructuringExpansionSelectExpr(Context, DD, Idx); + + Expr::EvalResult ER; + if (!Idx->EvaluateAsInt(ER, Context)) + llvm_unreachable("Failed to evaluate expansion index"); + + uint64_t I = ER.Val.getInt().getZExtValue(); + MarkAnyDeclReferenced(Idx->getBeginLoc(), DD, true); + if (auto *BD = DD->bindings()[I]; auto *HVD = BD->getHoldingVar()) + return HVD->getInit(); + else + return BD->getBinding(); +} + std::optional<uint64_t> Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { assert(!HasDependentSize(Expansion)); @@ -663,5 +749,9 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { return ER.Val.getInt().getZExtValue(); } - llvm_unreachable("TODO"); + if (auto *Destructuring = + dyn_cast<CXXDestructuringExpansionStmtPattern>(Expansion)) + return Destructuring->getDecompositionDecl()->bindings().size(); + + llvm_unreachable("Invalid expansion statement class"); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index ec9a14c4b26c7..60c9349254c94 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9295,28 +9295,51 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) { template <typename Derived> StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern( CXXExpansionStmtPattern *S) { + ExprResult ExpansionInitializer; + SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps; CXXExpansionStmtDecl *NewESD = nullptr; Stmt *Init = nullptr; DeclStmt *ExpansionVarStmt = nullptr; - Decl *ESD = - getDerived().TransformDecl(S->getDecl()->getLocation(), S->getDecl()); - if (!ESD || ESD->isInvalidDecl()) - return StmtError(); - NewESD = cast<CXXExpansionStmtDecl>(ESD); - Init = S->getInit(); - if (Init) { - StmtResult SR = getDerived().TransformStmt(Init); - if (SR.isInvalid()) + // Collect lifetime-extended temporaries in case this ends up being a + // destructuring expansion statement (for other kinds of expansion statements, + // this should make no difference since we ignore 'LifetimeExtendTemps' for + // those). + { + EnterExpressionEvaluationContext ExprEvalCtx( + SemaRef, SemaRef.currentEvaluationContext().Context); + SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true; + SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true; + Decl *ESD = + getDerived().TransformDecl(S->getDecl()->getLocation(), S->getDecl()); + if (!ESD || ESD->isInvalidDecl()) return StmtError(); - Init = SR.get(); - } + NewESD = cast<CXXExpansionStmtDecl>(ESD); - StmtResult ExpansionVar = - getDerived().TransformStmt(S->getExpansionVarStmt()); - if (ExpansionVar.isInvalid()) - return StmtError(); - ExpansionVarStmt = cast<DeclStmt>(ExpansionVar.get()); + Init = S->getInit(); + if (Init) { + StmtResult SR = getDerived().TransformStmt(Init); + if (SR.isInvalid()) + return StmtError(); + Init = SR.get(); + } + + StmtResult ExpansionVar = + getDerived().TransformStmt(S->getExpansionVarStmt()); + if (ExpansionVar.isInvalid()) + return StmtError(); + ExpansionVarStmt = cast<DeclStmt>(ExpansionVar.get()); + + if (S->isDependent()) { + ExpansionInitializer = + getDerived().TransformExpr(S->getExpansionInitializer()); + if (ExpansionInitializer.isInvalid()) + return StmtError(); + + LifetimeExtendTemps = + SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps; + } + } CXXExpansionStmtPattern *NewPattern = nullptr; if (S->isEnumerating()) { @@ -9353,7 +9376,12 @@ StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern( NewPattern = cast<CXXExpansionStmtPattern>(Res.get()); } else { - llvm_unreachable("TODO"); + // The only time we instantiate an expansion statement is if its expansion + // size is dependent (otherwise, we only instantiate the expansions and + // leave the underlying CXXExpansionStmtPattern as-is). Since destructuring + // expansion statements never have a dependent size, we should never get + // here. + llvm_unreachable("destructuring pattern should never be instantiated"); } StmtResult Body = getDerived().TransformStmt(S->getBody()); @@ -9384,8 +9412,23 @@ StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation( SmallVector<Stmt *> SharedStmts; SmallVector<Stmt *> Instantiations; - if (TransformStmts(SharedStmts, S->getSharedStmts())) - return StmtError(); + // Apply lifetime extension to the shared statements if this was a + // destructuring expansion statement. + { + EnterExpressionEvaluationContext ExprEvalCtx( + SemaRef, SemaRef.currentEvaluationContext().Context); + SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true; + SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true; + if (TransformStmts(SharedStmts, S->getSharedStmts())) + return StmtError(); + + if (S->shouldApplyLifetimeExtensionToSharedStmts()) { + auto *VD = + cast<VarDecl>(cast<DeclStmt>(SharedStmts.front())->getSingleDecl()); + SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension( + VD, SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps); + } + } if (TransformStmts(Instantiations, S->getInstantiations())) return StmtError(); >From a2c25a54b28371d15b266a19a119e73abcdfff37 Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Wed, 3 Dec 2025 18:23:18 +0100 Subject: [PATCH 2/6] Eliminate CXXExpansionInitListExpr --- clang/lib/Sema/SemaExpand.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index 315a4bdfd3a6c..45f99530c9f20 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -85,7 +85,7 @@ static bool HasDependentSize(const CXXExpansionStmtPattern *Pattern) { return true; case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: - llvm_unreachable("TODO"); + return false; } llvm_unreachable("invalid pattern kind"); >From 95b172a9e463b6717aa7569d843930e5bb1ec720 Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Wed, 3 Dec 2025 20:35:03 +0100 Subject: [PATCH 3/6] Fix more rebase issues --- clang/lib/Sema/TreeTransform.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 60c9349254c94..79187f58c232e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9361,15 +9361,10 @@ StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern( Range.getAs<DeclStmt>(), Begin.getAs<DeclStmt>(), End.getAs<DeclStmt>(), S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc()); } else if (S->isDependent()) { - ExprResult ExpansionInitializer = - getDerived().TransformExpr(S->getExpansionInitializer()); - if (ExpansionInitializer.isInvalid()) - return StmtError(); - StmtResult Res = SemaRef.BuildNonEnumeratingCXXExpansionStmtPattern( NewESD, Init, ExpansionVarStmt, ExpansionInitializer.get(), S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc(), - /*LifetimeExtendTemps=*/{}); + LifetimeExtendTemps); if (Res.isInvalid()) return StmtError(); >From fb19d23efc3b37f8d600b36337c3a1badcec4d02 Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Wed, 3 Dec 2025 20:44:27 +0100 Subject: [PATCH 4/6] Merge all pattern kinds into a single AST node --- clang/lib/Sema/SemaExpand.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index 45f99530c9f20..4c9eeac60cdae 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -437,8 +437,8 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( if (FinaliseExpansionVar(*this, ExpansionVar, Select)) return StmtError(); - return new (Context) CXXDestructuringExpansionStmtPattern( - ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc); + return CXXExpansionStmtPattern::CreateDestructuring( + Context, ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc); } StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { @@ -749,9 +749,6 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { return ER.Val.getInt().getZExtValue(); } - if (auto *Destructuring = - dyn_cast<CXXDestructuringExpansionStmtPattern>(Expansion)) - return Destructuring->getDecompositionDecl()->bindings().size(); - - llvm_unreachable("Invalid expansion statement class"); + assert(Expansion->isDestructuring()); + return Expansion->getDecompositionDecl()->bindings().size(); } >From d72cc782f95f6dd7be9139c6dd5dfeb422b52c27 Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Wed, 3 Dec 2025 22:04:53 +0100 Subject: [PATCH 5/6] Only use a single CXXExpansionSelectExpr --- clang/include/clang/Sema/Sema.h | 3 --- clang/lib/Sema/SemaExpand.cpp | 33 +++++++++++++++------------------ 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 48bf7ba1083b7..f5a36626a9dad 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -15712,9 +15712,6 @@ class Sema final : public SemaBase { ExprResult BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx); - ExprResult BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD, - Expr *Idx); - std::optional<uint64_t> ComputeExpansionSize(CXXExpansionStmtPattern *Expansion); ///@} diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index 4c9eeac60cdae..89b9f41cd3330 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -428,7 +428,19 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( if (DD->isInvalidDecl()) return StmtError(); - ExprResult Select = BuildCXXDestructuringExpansionSelectExpr(DD, Index); + // Synthesise an InitListExpr to store DREs to the BindingDecls; this + // essentially lets us desugar the expansion of a destructuring expansion + // statement to that of an enumerating expansion statement. + SmallVector<Expr *> Bindings; + for (BindingDecl *BD : DD->bindings()) { + auto *HVD = BD->getHoldingVar(); + Bindings.push_back(HVD ? HVD->getInit() : BD->getBinding()); + } + + ExprResult Select = BuildCXXExpansionSelectExpr( + new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc), + Index); + if (Select.isInvalid()) { ActOnInitializerError(ExpansionVar); return StmtError(); @@ -480,6 +492,8 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { Shared.push_back(Expansion->getEndVarStmt()); } else if (Expansion->isDestructuring()) { Shared.push_back(Expansion->getDecompositionDeclStmt()); + MarkAnyDeclReferenced(Exp->getBeginLoc(), Expansion->getDecompositionDecl(), + true); } // Return an empty statement if the range is empty. @@ -540,23 +554,6 @@ ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) { return Range->getInit(I); } -ExprResult Sema::BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD, - Expr *Idx) { - if (Idx->isValueDependent()) - return new (Context) CXXDestructuringExpansionSelectExpr(Context, DD, Idx); - - Expr::EvalResult ER; - if (!Idx->EvaluateAsInt(ER, Context)) - llvm_unreachable("Failed to evaluate expansion index"); - - uint64_t I = ER.Val.getInt().getZExtValue(); - MarkAnyDeclReferenced(Idx->getBeginLoc(), DD, true); - if (auto *BD = DD->bindings()[I]; auto *HVD = BD->getHoldingVar()) - return HVD->getInit(); - else - return BD->getBinding(); -} - std::optional<uint64_t> Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { assert(!HasDependentSize(Expansion)); >From 07e223d2c8deb49074017fa7c7c03c43a5191549 Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Wed, 3 Dec 2025 22:19:52 +0100 Subject: [PATCH 6/6] Rename a function and update a comment --- clang/lib/Sema/SemaExpand.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index 89b9f41cd3330..6fa1ad9856783 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -220,7 +220,7 @@ TryBuildIterableExpansionStmtInitializer(Sema &S, Expr *ExpansionInitializer, return Data; } -static StmtResult BuildDestructuringCXXExpansionStmt( +static StmtResult BuildDestructuringDecompositionDecl( Sema &S, Expr *ExpansionInitializer, SourceLocation ColonLoc, bool VarIsConstexpr, ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) { @@ -415,7 +415,7 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( } // If not, try destructuring. - StmtResult DecompDeclStmt = BuildDestructuringCXXExpansionStmt( + StmtResult DecompDeclStmt = BuildDestructuringDecompositionDecl( *this, ExpansionInitializer, ColonLoc, ExpansionVar->isConstexpr(), LifetimeExtendTemps); if (DecompDeclStmt.isInvalid()) { @@ -428,9 +428,9 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( if (DD->isInvalidDecl()) return StmtError(); - // Synthesise an InitListExpr to store DREs to the BindingDecls; this - // essentially lets us desugar the expansion of a destructuring expansion - // statement to that of an enumerating expansion statement. + // Synthesise an InitListExpr to store the bindings; this essentially lets us + // desugar the expansion of a destructuring expansion statement to that of an + // enumerating expansion statement. SmallVector<Expr *> Bindings; for (BindingDecl *BD : DD->bindings()) { auto *HVD = BD->getHoldingVar(); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
