https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/169685
>From e4abcd3e0fa975ea764e57a39f89bdf43f18bf67 Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Wed, 26 Nov 2025 17:00:57 +0100 Subject: [PATCH] [Clang] [C++26] Expansion Statements (Part 6) --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Sema/SemaExpand.cpp | 111 +++++++++++++++++- clang/lib/Sema/TreeTransform.h | 26 +++- 3 files changed, 130 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 9142afb95b89d..7f6fd47b3bb71 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3730,6 +3730,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/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index f821f73cb1e55..27878377f934f 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -86,7 +86,7 @@ static bool HasDependentSize(const DeclContext *CurContext, return true; case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: - llvm_unreachable("TODO"); + return false; } llvm_unreachable("invalid pattern kind"); @@ -274,6 +274,51 @@ static IterableExpansionStmtData TryBuildIterableExpansionStmtInitializer( return Data; } +static StmtResult BuildDestructuringDecompositionDecl( + 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->getParent(), + /*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) { @@ -435,8 +480,59 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( Data.IterDecl, LParenLoc, ColonLoc, RParenLoc); } - Diag(ESD->getLocation(), diag::err_expansion_statements_todo); - return StmtError(); + // If not, try destructuring. + StmtResult DecompDeclStmt = BuildDestructuringDecompositionDecl( + *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(); + + // 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()) { + Expr *Element = BuildDeclRefExpr(BD, BD->getType().getNonReferenceType(), + VK_LValue, ColonLoc); + + // CWG 3149: If the expansion-initializer is an lvalue, then vi is ui; + // otherwise, vi is static_cast<decltype(ui)&&>(ui). + if (!ExpansionInitializer->isLValue()) { + QualType Ty = + BuildReferenceType(getDecltypeForExpr(Element), /*LValueRef=*/false, + ColonLoc, /*Entity=*/DeclarationName()); + TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(Ty); + ExprResult Cast = BuildCXXNamedCast( + ColonLoc, tok::kw_static_cast, TSI, Element, + SourceRange(ColonLoc, ColonLoc), SourceRange(ColonLoc, ColonLoc)); + assert(!Cast.isInvalid() && "cast to rvalue reference type failed?"); + Element = Cast.get(); + } + + Bindings.push_back(Element); + } + + ExprResult Select = BuildCXXExpansionSelectExpr( + new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc), + Index); + + if (Select.isInvalid()) { + ActOnInitializerError(ExpansionVar); + return StmtError(); + } + + if (FinalizeExpansionVar(*this, ExpansionVar, Select)) + return StmtError(); + + return CXXExpansionStmtPattern::CreateDestructuring( + Context, ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc); } StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { @@ -469,8 +565,10 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { if (Expansion->isIterating()) { Shared.push_back(Expansion->getRangeVarStmt()); Shared.push_back(Expansion->getBeginVarStmt()); - } else { - assert(Expansion->isEnumerating() && "TODO"); + } else if (Expansion->isDestructuring()) { + Shared.push_back(Expansion->getDecompositionDeclStmt()); + MarkAnyDeclReferenced(Exp->getBeginLoc(), Expansion->getDecompositionDecl(), + true); } // Return an empty statement if the range is empty. @@ -722,5 +820,6 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { return ER.Val.getInt().getZExtValue(); } - llvm_unreachable("TODO"); + assert(Expansion->isDestructuring()); + return Expansion->getDecompositionDecl()->bindings().size(); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 864667d784e6d..b3984a47eac98 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9479,7 +9479,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()); @@ -9510,8 +9515,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(); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
