From 778c6a7efd7a4f330ab81d8aae7cddebe516c8ec Mon Sep 17 00:00:00 2001 From: Sirraide Date: Wed, 26 Nov 2025 16:18:02 +0100 Subject: [PATCH 1/9] [Clang] [C++26] Expansion Statements (Part 5) --- .../clang/Basic/DiagnosticSemaKinds.td | 7 + clang/include/clang/Sema/Sema.h | 6 + clang/lib/Sema/SemaExpand.cpp | 257 +++++++++++++++++- clang/lib/Sema/SemaStmt.cpp | 13 +- clang/lib/Sema/TreeTransform.h | 14 + 5 files changed, 291 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index fea25ef6a0734..0ce3d8164d955 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -165,6 +165,10 @@ def err_ice_too_large : Error< def err_expr_not_string_literal : Error<"expression is not a string literal">; def note_constexpr_assert_failed : Note< "assertion failed during evaluation of constant expression">; +def err_expansion_size_expr_not_ice : Error< + "expansion size is not a constant expression">; +def err_expansion_size_negative : Error< + "expansion size must not be negative (was %0)">; // Semantic analysis of constant literals. def ext_predef_outside_function : Warning< @@ -3710,6 +3714,9 @@ def err_conflicting_codeseg_attribute : Error< def warn_duplicate_codeseg_attribute : Warning< "duplicate code segment specifiers">, InGroup
; +def err_expansion_stmt_lambda : Error< + "cannot expand lambda closure type">; + def err_attribute_patchable_function_entry_invalid_section : Error<"section argument to 'patchable_function_entry' attribute is not " "valid for this target: %0">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5dee71b23422e..0adc570c73011 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -15705,6 +15705,12 @@ class Sema final : public SemaBase { SourceLocation ColonLoc, SourceLocation RParenLoc); + StmtResult BuildNonEnumeratingCXXExpansionStmtPattern( + CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt, + Expr *ExpansionInitializer, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc, + ArrayRef LifetimeExtendTemps = {}); + ExprResult BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx); std::optional diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index a0f5e852ebdb1..4e392e33578e2 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -24,6 +24,25 @@ using namespace clang; using namespace sema; +namespace { +struct IterableExpansionStmtData { + enum class State { + NotIterable, + Error, + Ok, + }; + + DeclStmt *RangeDecl = nullptr; + DeclStmt *BeginDecl = nullptr; + DeclStmt *EndDecl = nullptr; + Expr *Initializer = nullptr; + State TheState = State::NotIterable; + + bool isIterable() const { return TheState == State::Ok; } + bool hasError() { return TheState == State::Error; } +}; +} // namespace + // Build a 'DeclRefExpr' designating the template parameter '__N'. static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) { return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(), @@ -55,15 +74,134 @@ static bool HasDependentSize(const CXXExpansionStmtPattern *Pattern) { return InitListContainsPack(SelectExpr->getRangeExpr()); } - case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: - case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: + case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: { + const Expr *Begin = Pattern->getBeginVar()->getInit(); + const Expr *End = Pattern->getEndVar()->getInit(); + return Begin->isInstantiationDependent() || End->isInstantiationDependent(); + } + case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent: + return true; + + case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: llvm_unreachable("TODO"); } llvm_unreachable("invalid pattern kind"); } +static IterableExpansionStmtData +TryBuildIterableExpansionStmtInitializer(Sema &S, Expr *ExpansionInitializer, + Expr *Index, SourceLocation ColonLoc, + bool VarIsConstexpr) { + IterableExpansionStmtData Data; + + // C++26 [stmt.expand]p3: An expression is expansion-iterable if it does not + // have array type [...] + QualType Ty = ExpansionInitializer->getType().getNonReferenceType(); + if (Ty->isArrayType()) + return Data; + + // Lookup member and ADL 'begin()'/'end()'. Only check if they exist; even if + // they're deleted, inaccessible, etc., this is still an iterating expansion + // statement, albeit an ill-formed one. + DeclarationNameInfo BeginName(&S.PP.getIdentifierTable().get("begin"), + ColonLoc); + DeclarationNameInfo EndName(&S.PP.getIdentifierTable().get("end"), ColonLoc); + + // Try member lookup first. + bool FoundBeginEnd = false; + if (auto *Record = Ty->getAsCXXRecordDecl()) { + LookupResult BeginLR(S, BeginName, Sema::LookupMemberName); + LookupResult EndLR(S, EndName, Sema::LookupMemberName); + FoundBeginEnd = S.LookupQualifiedName(BeginLR, Record) && + S.LookupQualifiedName(EndLR, Record); + } + + // Try ADL. + // + // If overload resolution for 'begin()' *and* 'end()' succeeds (irrespective + // of whether it results in a usable candidate), then assume this is an + // iterating expansion statement. + auto HasADLCandidate = [&](DeclarationName Name) { + OverloadCandidateSet Candidates(ColonLoc, OverloadCandidateSet::CSK_Normal); + OverloadCandidateSet::iterator Best; + + S.AddArgumentDependentLookupCandidates(Name, ColonLoc, ExpansionInitializer, + /*ExplicitTemplateArgs=*/nullptr, + Candidates); + + return Candidates.BestViableFunction(S, ColonLoc, Best) != + OR_No_Viable_Function; + }; + + if (!FoundBeginEnd && (!HasADLCandidate(BeginName.getName()) || + !HasADLCandidate(EndName.getName()))) + return Data; + + 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); + + // Ok, we know that this is supposed to be an iterable expansion statement; + // delegate to the for-range code to build the range/begin/end variables. + // + // Any failure at this point is a hard error. + Data.TheState = IterableExpansionStmtData::State::Error; + Scope *Scope = S.getCurScope(); + + // TODO: CWG 3131 changes how this range is declared. + StmtResult Var = S.BuildCXXForRangeRangeVar(Scope, ExpansionInitializer, + /*ForExpansionStmt=*/true); + if (Var.isInvalid()) + return Data; + + auto *RangeVar = cast(Var.get()); + Sema::ForRangeBeginEndInfo Info = S.BuildCXXForRangeBeginEndVars( + Scope, cast(RangeVar->getSingleDecl()), ColonLoc, + /*CoawaitLoc=*/{}, + /*LifetimeExtendTemps=*/{}, Sema::BFRK_Build, /*ForExpansionStmt=*/true); + + if (!Info.isValid()) + return Data; + + StmtResult BeginStmt = S.ActOnDeclStmt( + S.ConvertDeclToDeclGroup(Info.BeginVar), ColonLoc, ColonLoc); + StmtResult EndStmt = S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(Info.EndVar), + ColonLoc, ColonLoc); + if (BeginStmt.isInvalid() || EndStmt.isInvalid()) + return Data; + + // Build '*(begin + i)'. + DeclRefExpr *Begin = S.BuildDeclRefExpr( + Info.BeginVar, Info.BeginVar->getType().getNonReferenceType(), VK_LValue, + ColonLoc); + + ExprResult BeginPlusI = + S.ActOnBinOp(Scope, ColonLoc, tok::plus, Begin, Index); + if (BeginPlusI.isInvalid()) + return Data; + + ExprResult Deref = + S.ActOnUnaryOp(Scope, ColonLoc, tok::star, BeginPlusI.get()); + if (Deref.isInvalid()) + return Data; + + Deref = S.MaybeCreateExprWithCleanups(Deref.get()); + Data.BeginDecl = BeginStmt.getAs(); + Data.EndDecl = EndStmt.getAs(); + Data.RangeDecl = RangeVar; + Data.Initializer = Deref.get(); + Data.TheState = IterableExpansionStmtData::State::Ok; + return Data; +} + CXXExpansionStmtDecl * Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth, SourceLocation TemplateKWLoc) { @@ -134,8 +272,26 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern( ColonLoc, RParenLoc); } - Diag(ESD->getLocation(), diag::err_expansion_statements_todo); - return StmtError(); + if (ExpansionInitializer->hasPlaceholderType()) { + ExprResult R = CheckPlaceholderExpr(ExpansionInitializer); + if (R.isInvalid()) + return StmtError(); + ExpansionInitializer = R.get(); + } + + if (DiagnoseUnexpandedParameterPack(ExpansionInitializer)) + return StmtError(); + + // Reject lambdas early. + if (auto *RD = ExpansionInitializer->getType()->getAsCXXRecordDecl(); + RD && RD->isLambda()) { + Diag(ExpansionInitializer->getBeginLoc(), diag::err_expansion_stmt_lambda); + return StmtError(); + } + + return BuildNonEnumeratingCXXExpansionStmtPattern( + ESD, Init, DS, ExpansionInitializer, LParenLoc, ColonLoc, RParenLoc, + LifetimeExtendTemps); } StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern( @@ -146,6 +302,43 @@ StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern( cast(ExpansionVar), LParenLoc, ColonLoc, RParenLoc); } +StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( + CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt, + Expr *ExpansionInitializer, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc, + ArrayRef LifetimeExtendTemps) { + VarDecl *ExpansionVar = cast(ExpansionVarStmt->getSingleDecl()); + + if (ExpansionInitializer->isTypeDependent()) { + ActOnDependentForRangeInitializer(ExpansionVar, BFRK_Build); + return new (Context) CXXDependentExpansionStmtPattern( + ESD, Init, ExpansionVarStmt, ExpansionInitializer, LParenLoc, ColonLoc, + RParenLoc); + } + + // Otherwise, if it can be an iterating expansion statement, it is one. + DeclRefExpr *Index = BuildIndexDRE(*this, ESD); + IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer( + *this, ExpansionInitializer, Index, ColonLoc, + ExpansionVar->isConstexpr()); + if (Data.hasError()) { + ActOnInitializerError(ExpansionVar); + return StmtError(); + } + + if (Data.isIterable()) { + if (FinaliseExpansionVar(*this, ExpansionVar, Data.Initializer)) + return StmtError(); + + return new (Context) CXXIteratingExpansionStmtPattern( + ESD, Init, ExpansionVarStmt, Data.RangeDecl, Data.BeginDecl, + Data.EndDecl, LParenLoc, ColonLoc, RParenLoc); + } + + Diag(ESD->getLocation(), diag::err_expansion_statements_todo); + return StmtError(); +} + StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { if (!Exp || !Body) return StmtError(); @@ -168,7 +361,13 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { if (Expansion->getInit()) Shared.push_back(Expansion->getInit()); - assert(Expansion->isEnumerating() && "TODO"); + if (Expansion->isIterating()) { + Shared.push_back(Expansion->getRangeVarStmt()); + Shared.push_back(Expansion->getBeginVarStmt()); + Shared.push_back(Expansion->getEndVarStmt()); + } else { + assert(Expansion->isEnumerating() && "TODO"); + } // Return an empty statement if the range is empty. if (*NumInstantiations == 0) { @@ -243,5 +442,53 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { ->getRangeExpr() ->getNumInits(); + // By [stmt.expand]5.2, N is the result of evaluating the expression + // + // [] consteval { + // std::ptrdiff_t result = 0; + // for (auto i = begin; i != end; ++i) ++result; + // return result; + // }() + // TODO: CWG 3131 changes this lambda a bit. + if (auto *Iterating = dyn_cast(Expansion)) { + EnterExpressionEvaluationContext ExprEvalCtx( + *this, ExpressionEvaluationContext::ConstantEvaluated); + + // FIXME: Actually do that; unfortunately, conjuring a lambda out of thin + // air in Sema is a massive pain, so for now just cheat by computing + // 'end - begin'. + SourceLocation Loc = Iterating->getColonLoc(); + DeclRefExpr *Begin = BuildDeclRefExpr( + Iterating->getBeginVar(), + Iterating->getBeginVar()->getType().getNonReferenceType(), VK_LValue, + Loc); + + DeclRefExpr *End = BuildDeclRefExpr( + Iterating->getEndVar(), + Iterating->getEndVar()->getType().getNonReferenceType(), VK_LValue, + Loc); + + ExprResult N = ActOnBinOp(getCurScope(), Loc, tok::minus, End, Begin); + if (N.isInvalid()) + return std::nullopt; + + Expr::EvalResult ER; + SmallVector Notes; + ER.Diag = &Notes; + if (!N.get()->EvaluateAsInt(ER, Context)) { + Diag(Loc, diag::err_expansion_size_expr_not_ice); + for (const auto &[Location, PDiag] : Notes) + Diag(Location, PDiag); + return std::nullopt; + } + + if (ER.Val.getInt().isNegative()) { + Diag(Loc, diag::err_expansion_size_negative) << ER.Val.getInt(); + return std::nullopt; + } + + return ER.Val.getInt().getZExtValue(); + } + llvm_unreachable("TODO"); } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index a9fa0c3910c86..5a4c2ee9dd06f 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2411,6 +2411,11 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E, /// Build a variable declaration for a for-range statement. VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, QualType Type, StringRef Name, bool ForExpansionStmt) { + // Making the variable constexpr doesn't automatically add 'const' to the + // type, so do that now. + if (ForExpansionStmt && !Type->isReferenceType()) + Type = Type.withConst(); + DeclContext *DC = SemaRef.CurContext; IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name); TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc); @@ -2418,6 +2423,9 @@ VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, QualType Type, TInfo, SC_None); Decl->setImplicit(); Decl->setCXXForRangeImplicitVar(true); + if (ForExpansionStmt) + // CWG 3044: Do not make the variable 'static'. + Decl->setConstexpr(true); return Decl; } } @@ -2731,7 +2739,10 @@ Sema::ForRangeBeginEndInfo Sema::BuildCXXForRangeBeginEndVars( return {}; // P2718R0 - Lifetime extension in range-based for loops. - if (getLangOpts().CPlusPlus23) + // + // CWG 3043 – Do not apply lifetime extension to iterating + // expansion statements. + if (getLangOpts().CPlusPlus23 && !ForExpansionStmt) ApplyForRangeOrExpansionStatementLifetimeExtension(RangeVar, LifetimeExtendTemps); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 7ed1b91ee733a..4d85bc4f76c72 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9325,6 +9325,20 @@ StmtResult TreeTransform::TransformCXXExpansionStmtPattern( NewPattern = CXXExpansionStmtPattern::CreateEnumerating( SemaRef.Context, NewESD, Init, ExpansionVarStmt, S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc()); + } else if (S->isIterating()) { + StmtResult Range = getDerived().TransformStmt(S->getRangeVarStmt()); + if (Range.isInvalid()) + return StmtError(); + + StmtResult Begin = getDerived().TransformStmt(S->getBeginVarStmt()); + StmtResult End = getDerived().TransformStmt(S->getEndVarStmt()); + if (Begin.isInvalid() || End.isInvalid()) + return StmtError(); + + NewPattern = CXXExpansionStmtPattern::CreateIterating( + SemaRef.Context, NewESD, Init, ExpansionVarStmt, + Range.getAs(), Begin.getAs(), End.getAs(), + S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc()); } else { llvm_unreachable("TODO"); } From b57428b28d4fbc0637dc5900be164bd819565836 Mon Sep 17 00:00:00 2001 From: Sirraide Date: Mon, 1 Dec 2025 19:12:52 +0100 Subject: [PATCH 2/9] Reject incomplete types and vlas --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++++ clang/lib/Sema/SemaExpand.cpp | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0ce3d8164d955..ef7321ed0dfa0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3714,6 +3714,10 @@ def err_conflicting_codeseg_attribute : Error< def warn_duplicate_codeseg_attribute : Warning< "duplicate code segment specifiers">, InGroup
; +def err_expansion_stmt_vla : Error< + "cannot expand variable length array type %0">; +def err_expansion_stmt_incomplete : Error< + "cannot expand expression of incomplete type %0">; def err_expansion_stmt_lambda : Error< "cannot expand lambda closure type">; diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index 4e392e33578e2..a8496720ad39e 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -316,6 +316,17 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( RParenLoc); } + if (RequireCompleteType(ExpansionInitializer->getExprLoc(), + ExpansionInitializer->getType(), + diag::err_expansion_stmt_incomplete)) + return StmtError(); + + if (ExpansionInitializer->getType()->isVariableArrayType()) { + Diag(ExpansionInitializer->getExprLoc(), diag::err_expansion_stmt_vla) + << ExpansionInitializer->getType(); + return StmtError(); + } + // Otherwise, if it can be an iterating expansion statement, it is one. DeclRefExpr *Index = BuildIndexDRE(*this, ESD); IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer( From def1353c1f8a5ef86d7b8843fa0f53dab173dc1f Mon Sep 17 00:00:00 2001 From: Sirraide Date: Tue, 2 Dec 2025 21:52:48 +0100 Subject: [PATCH 3/9] Reject lambdas in Build instead of ActOn --- clang/lib/Sema/SemaExpand.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index a8496720ad39e..bea0374b53f41 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -282,13 +282,6 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern( if (DiagnoseUnexpandedParameterPack(ExpansionInitializer)) return StmtError(); - // Reject lambdas early. - if (auto *RD = ExpansionInitializer->getType()->getAsCXXRecordDecl(); - RD && RD->isLambda()) { - Diag(ExpansionInitializer->getBeginLoc(), diag::err_expansion_stmt_lambda); - return StmtError(); - } - return BuildNonEnumeratingCXXExpansionStmtPattern( ESD, Init, DS, ExpansionInitializer, LParenLoc, ColonLoc, RParenLoc, LifetimeExtendTemps); @@ -327,6 +320,13 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( return StmtError(); } + // Reject lambdas early. + if (auto *RD = ExpansionInitializer->getType()->getAsCXXRecordDecl(); + RD && RD->isLambda()) { + Diag(ExpansionInitializer->getBeginLoc(), diag::err_expansion_stmt_lambda); + return StmtError(); + } + // Otherwise, if it can be an iterating expansion statement, it is one. DeclRefExpr *Index = BuildIndexDRE(*this, ESD); IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer( From 41889bc78926d404a8f619f8d75c31a9a9500ea2 Mon Sep 17 00:00:00 2001 From: Sirraide Date: Wed, 3 Dec 2025 01:50:44 +0100 Subject: [PATCH 4/9] Properly compute iterating expansion stmt size --- clang/lib/Sema/SemaExpand.cpp | 191 +++++++++++++++++++++++++++++----- 1 file changed, 167 insertions(+), 24 deletions(-) diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index bea0374b53f41..be772a9ce7c6b 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -21,6 +21,8 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/Template.h" +#include + using namespace clang; using namespace sema; @@ -302,6 +304,13 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( ArrayRef LifetimeExtendTemps) { VarDecl *ExpansionVar = cast(ExpansionVarStmt->getSingleDecl()); + // Reject lambdas early. + if (auto *RD = ExpansionInitializer->getType()->getAsCXXRecordDecl(); + RD && RD->isLambda()) { + Diag(ExpansionInitializer->getBeginLoc(), diag::err_expansion_stmt_lambda); + return StmtError(); + } + if (ExpansionInitializer->isTypeDependent()) { ActOnDependentForRangeInitializer(ExpansionVar, BFRK_Build); return new (Context) CXXDependentExpansionStmtPattern( @@ -320,13 +329,6 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( return StmtError(); } - // Reject lambdas early. - if (auto *RD = ExpansionInitializer->getType()->getAsCXXRecordDecl(); - RD && RD->isLambda()) { - Diag(ExpansionInitializer->getBeginLoc(), diag::err_expansion_stmt_lambda); - return StmtError(); - } - // Otherwise, if it can be an iterating expansion statement, it is one. DeclRefExpr *Index = BuildIndexDRE(*this, ESD); IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer( @@ -362,6 +364,18 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { if (HasDependentSize(Expansion)) return Expansion; + // 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); + + // Even if the size isn't technically dependent, delay expansion until + // we're no longer in a template if this is an iterating expansion statement + // since evaluating a lambda declared in a template doesn't work too well. + if (CurContext->isDependentContext() && + isa(Expansion)) + return Expansion; + // This can fail if this is an iterating expansion statement. std::optional NumInstantiations = ComputeExpansionSize(Expansion); if (!NumInstantiations) @@ -399,11 +413,6 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { SmallVector 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); @@ -462,42 +471,176 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { // }() // TODO: CWG 3131 changes this lambda a bit. if (auto *Iterating = dyn_cast(Expansion)) { + SourceLocation Loc = Expansion->getColonLoc(); EnterExpressionEvaluationContext ExprEvalCtx( *this, ExpressionEvaluationContext::ConstantEvaluated); - // FIXME: Actually do that; unfortunately, conjuring a lambda out of thin - // air in Sema is a massive pain, so for now just cheat by computing - // 'end - begin'. - SourceLocation Loc = Iterating->getColonLoc(); + // This is mostly copied from ParseLambdaExpressionAfterIntroducer(). + ParseScope LambdaScope(*this, Scope::LambdaScope | Scope::DeclScope | + Scope::FunctionDeclarationScope | + Scope::FunctionPrototypeScope); + AttributeFactory AttrFactory; + LambdaIntroducer Intro; + Intro.Range = SourceRange(Loc, Loc); + Intro.Default = LCD_ByRef; // CWG 3131 + Intro.DefaultLoc = Loc; + DeclSpec DS(AttrFactory); + Declarator D(DS, ParsedAttributesView::none(), + DeclaratorContext::LambdaExpr); + PushLambdaScope(); + ActOnLambdaExpressionAfterIntroducer(Intro, getCurScope()); + + // Make the lambda 'consteval'. + { + ParseScope Prototype(*this, Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope | + Scope::DeclScope); + const char* PrevSpec = nullptr; + unsigned DiagId = 0; + DS.SetConstexprSpec(ConstexprSpecKind::Consteval, Loc, PrevSpec, DiagId); + assert(DiagId == 0 && PrevSpec == nullptr); + ActOnLambdaClosureParameters(getCurScope(), /*ParamInfo=*/{}); + ActOnLambdaClosureQualifiers(Intro, /*MutableLoc=*/SourceLocation()); + } + + ParseScope BodyScope(*this, Scope::BlockScope | Scope::FnScope | + Scope::DeclScope | + Scope::CompoundStmtScope); + + ActOnStartOfLambdaDefinition(Intro, D, DS); + + // Enter the compound statement that is the lambda body. + ActOnStartOfCompoundStmt(/*IsStmtExpr=*/false); + ActOnAfterCompoundStatementLeadingPragmas(); + auto PopScopesOnReturn = llvm::make_scope_exit([&] { + ActOnFinishOfCompoundStmt(); + ActOnLambdaError(Loc, getCurScope()); + }); + + // std::ptrdiff_t result = 0; + QualType PtrDiffT = Context.getPointerDiffType(); + VarDecl *ResultVar = VarDecl::Create( + Context, CurContext, Loc, Loc, &PP.getIdentifierTable().get("__result"), + PtrDiffT, Context.getTrivialTypeSourceInfo(PtrDiffT, Loc), SC_None); + Expr *Zero = ActOnIntegerConstant(Loc, 0).get(); + AddInitializerToDecl(ResultVar, Zero, false); + StmtResult ResultVarStmt = + ActOnDeclStmt(ConvertDeclToDeclGroup(ResultVar), Loc, Loc); + if (ResultVarStmt.isInvalid() || ResultVar->isInvalidDecl()) + return std::nullopt; + + // Start the for loop. + ParseScope ForScope(*this, Scope::DeclScope | Scope::ControlScope); + + // auto i = begin; + VarDecl *IterationVar = VarDecl::Create( + Context, CurContext, Loc, Loc, &PP.getIdentifierTable().get("__i"), + Context.getAutoDeductType(), + Context.getTrivialTypeSourceInfo(Context.getAutoDeductType(), Loc), + SC_None); DeclRefExpr *Begin = BuildDeclRefExpr( Iterating->getBeginVar(), Iterating->getBeginVar()->getType().getNonReferenceType(), VK_LValue, Loc); + AddInitializerToDecl(IterationVar, Begin, false); + StmtResult IterationVarStmt = + ActOnDeclStmt(ConvertDeclToDeclGroup(IterationVar), Loc, Loc); + if (IterationVarStmt.isInvalid() || IterationVar->isInvalidDecl()) + return std::nullopt; + // i != end + DeclRefExpr *IterationVarDeclRef = BuildDeclRefExpr( + IterationVar, IterationVar->getType().getNonReferenceType(), VK_LValue, + Loc); DeclRefExpr *End = BuildDeclRefExpr( Iterating->getEndVar(), Iterating->getEndVar()->getType().getNonReferenceType(), VK_LValue, Loc); + ExprResult NotEqual = ActOnBinOp(getCurScope(), Loc, tok::exclaimequal, + IterationVarDeclRef, End); + if (NotEqual.isInvalid()) + return std::nullopt; + ConditionResult Condition = ActOnCondition( + getCurScope(), Loc, NotEqual.get(), ConditionKind::Boolean, + /*MissingOk=*/false); + if (Condition.isInvalid()) + return std::nullopt; + + // ++i + IterationVarDeclRef = BuildDeclRefExpr( + IterationVar, IterationVar->getType().getNonReferenceType(), VK_LValue, + Loc); + ExprResult Increment = + ActOnUnaryOp(getCurScope(), Loc, tok::plusplus, IterationVarDeclRef); + if (Increment.isInvalid()) + return std::nullopt; + FullExprArg ThirdPart = MakeFullDiscardedValueExpr(Increment.get()); + + // Enter the body of the for loop. + ParseScope InnerScope(*this, Scope::DeclScope); + getCurScope()->decrementMSManglingNumber(); + + // ++result; + DeclRefExpr *ResultDeclRef = BuildDeclRefExpr( + ResultVar, ResultVar->getType().getNonReferenceType(), VK_LValue, Loc); + ExprResult IncrementResult = + ActOnUnaryOp(getCurScope(), Loc, tok::plusplus, ResultDeclRef); + if (IncrementResult.isInvalid()) + return std::nullopt; + StmtResult IncrementStmt = ActOnExprStmt(IncrementResult.get()); + if (IncrementStmt.isInvalid()) + return std::nullopt; - ExprResult N = ActOnBinOp(getCurScope(), Loc, tok::minus, End, Begin); - if (N.isInvalid()) + // Exit the for loop. + InnerScope.Exit(); + ForScope.Exit(); + StmtResult ForLoop = + ActOnForStmt(Loc, Loc, IterationVarStmt.get(), Condition, ThirdPart, + Loc, IncrementStmt.get()); + if (ForLoop.isInvalid()) + return std::nullopt; + + // return result; + ResultDeclRef = BuildDeclRefExpr( + ResultVar, ResultVar->getType().getNonReferenceType(), VK_LValue, Loc); + StmtResult Return = ActOnReturnStmt(Loc, ResultDeclRef, getCurScope()); + if (Return.isInvalid()) + return std::nullopt; + + // Finally, we can build the compound statement that is the lambda body. + StmtResult LambdaBody = ActOnCompoundStmt( + Loc, Loc, {ResultVarStmt.get(), ForLoop.get(), Return.get()}, + /*isStmtExpr=*/false); + if (LambdaBody.isInvalid()) + return std::nullopt; + + ActOnFinishOfCompoundStmt(); + BodyScope.Exit(); + LambdaScope.Exit(); + PopScopesOnReturn.release(); + ExprResult Lambda = ActOnLambdaExpr(Loc, LambdaBody.get()); + if (Lambda.isInvalid()) + return std::nullopt; + + // Invoke the lambda. + ExprResult Call = + ActOnCallExpr(getCurScope(), Lambda.get(), Loc, /*ArgExprs=*/{}, Loc); + if (Call.isInvalid()) return std::nullopt; Expr::EvalResult ER; SmallVector Notes; ER.Diag = &Notes; - if (!N.get()->EvaluateAsInt(ER, Context)) { + if (!Call.get()->EvaluateAsInt(ER, Context)) { Diag(Loc, diag::err_expansion_size_expr_not_ice); for (const auto &[Location, PDiag] : Notes) Diag(Location, PDiag); return std::nullopt; } - if (ER.Val.getInt().isNegative()) { - Diag(Loc, diag::err_expansion_size_negative) << ER.Val.getInt(); - return std::nullopt; - } - + // It shouldn't be possible for this to be negative since we compute this + // via the built-in '++' on a ptrdiff_t. + assert(ER.Val.getInt().isNonNegative()); return ER.Val.getInt().getZExtValue(); } From bcfbabc1709405490bc15a8fc1aa97b447d23d7b Mon Sep 17 00:00:00 2001 From: Sirraide Date: Wed, 3 Dec 2025 01:52:54 +0100 Subject: [PATCH 5/9] Remove unused diagnostic --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ef7321ed0dfa0..a92a3a6a7c331 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -167,8 +167,6 @@ def note_constexpr_assert_failed : Note< "assertion failed during evaluation of constant expression">; def err_expansion_size_expr_not_ice : Error< "expansion size is not a constant expression">; -def err_expansion_size_negative : Error< - "expansion size must not be negative (was %0)">; // Semantic analysis of constant literals. def ext_predef_outside_function : Warning< From 352e26fa25f6663bf548fea70c26626da5beb4fe Mon Sep 17 00:00:00 2001 From: Sirraide Date: Wed, 3 Dec 2025 01:53:24 +0100 Subject: [PATCH 6/9] Formatting --- clang/lib/Sema/SemaExpand.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index be772a9ce7c6b..76c562f538400 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -20,8 +20,7 @@ #include "clang/Sema/Overload.h" #include "clang/Sema/Sema.h" #include "clang/Sema/Template.h" - -#include +#include "llvm/ADT/ScopeExit.h" using namespace clang; using namespace sema; @@ -493,9 +492,9 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { // Make the lambda 'consteval'. { ParseScope Prototype(*this, Scope::FunctionPrototypeScope | - Scope::FunctionDeclarationScope | - Scope::DeclScope); - const char* PrevSpec = nullptr; + Scope::FunctionDeclarationScope | + Scope::DeclScope); + const char *PrevSpec = nullptr; unsigned DiagId = 0; DS.SetConstexprSpec(ConstexprSpecKind::Consteval, Loc, PrevSpec, DiagId); assert(DiagId == 0 && PrevSpec == nullptr); From 4c1b3796efde5aee0b3398975be06d9691cea6e1 Mon Sep 17 00:00:00 2001 From: Sirraide Date: Wed, 3 Dec 2025 03:19:19 +0100 Subject: [PATCH 7/9] CWG 3131 --- clang/lib/Sema/SemaExpand.cpp | 107 ++++++++++++++++++++-------------- clang/lib/Sema/SemaStmt.cpp | 18 ++++-- 2 files changed, 78 insertions(+), 47 deletions(-) diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index 76c562f538400..13d981d7b426a 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -157,17 +157,34 @@ TryBuildIterableExpansionStmtInitializer(Sema &S, Expr *ExpansionInitializer, Data.TheState = IterableExpansionStmtData::State::Error; Scope *Scope = S.getCurScope(); - // TODO: CWG 3131 changes how this range is declared. - StmtResult Var = S.BuildCXXForRangeRangeVar(Scope, ExpansionInitializer, - /*ForExpansionStmt=*/true); + // CWG 3131: The declaration of 'range' is of the form + // + // constexpr[opt] decltype(auto) range = (expansion-initializer); + // + // where 'constexpr' is present iff the for-range-declaration is 'constexpr'. + StmtResult Var = S.BuildCXXForRangeRangeVar( + Scope, S.ActOnParenExpr(ColonLoc, ColonLoc, ExpansionInitializer).get(), + S.Context.getAutoType(QualType(), AutoTypeKeyword::DecltypeAuto, + /*IsDependent*/ false), + VarIsConstexpr); if (Var.isInvalid()) return Data; + // CWG 3131: Discussion around this core issue (though as of the time of + // writing not the resolution itself) suggests that the other variables we + // create here should likewise be 'constexpr' iff the range variable is + // declared 'constexpr'. + // + // FIXME: As of CWG 3131, 'end' is no longer used outside the lambda that + // performs the size calculation (despite that, CWG 3131 currently still + // lists it in the generated code, but this is likely an oversight). Ideally, + // we should only create 'begin' here instead, but that requires another + // substantial refactor of the for-range code. auto *RangeVar = cast(Var.get()); Sema::ForRangeBeginEndInfo Info = S.BuildCXXForRangeBeginEndVars( Scope, cast(RangeVar->getSingleDecl()), ColonLoc, /*CoawaitLoc=*/{}, - /*LifetimeExtendTemps=*/{}, Sema::BFRK_Build, /*ForExpansionStmt=*/true); + /*LifetimeExtendTemps=*/{}, Sema::BFRK_Build, VarIsConstexpr); if (!Info.isValid()) return Data; @@ -269,6 +286,10 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern( // 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). + // + // TODO: CWG 3131 makes it so the 'range' variable of an iterating + // expansion statement need no longer be 'constexpr'... so do we want + // lifetime extension for iterating expansion statements after all? return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc, ColonLoc, RParenLoc); } @@ -461,14 +482,15 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { ->getRangeExpr() ->getNumInits(); - // By [stmt.expand]5.2, N is the result of evaluating the expression + // CWG 3131: N is the result of evaluating the expression // - // [] consteval { + // [&] consteval { // std::ptrdiff_t result = 0; - // for (auto i = begin; i != end; ++i) ++result; + // auto b = begin-expr; + // auto e = end-expr; + // for (; b != e; ++b) ++result; // return result; // }() - // TODO: CWG 3131 changes this lambda a bit. if (auto *Iterating = dyn_cast(Expansion)) { SourceLocation Loc = Expansion->getColonLoc(); EnterExpressionEvaluationContext ExprEvalCtx( @@ -531,32 +553,32 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { // Start the for loop. ParseScope ForScope(*this, Scope::DeclScope | Scope::ControlScope); - // auto i = begin; - VarDecl *IterationVar = VarDecl::Create( - Context, CurContext, Loc, Loc, &PP.getIdentifierTable().get("__i"), - Context.getAutoDeductType(), - Context.getTrivialTypeSourceInfo(Context.getAutoDeductType(), Loc), - SC_None); - DeclRefExpr *Begin = BuildDeclRefExpr( - Iterating->getBeginVar(), - Iterating->getBeginVar()->getType().getNonReferenceType(), VK_LValue, - Loc); - AddInitializerToDecl(IterationVar, Begin, false); - StmtResult IterationVarStmt = - ActOnDeclStmt(ConvertDeclToDeclGroup(IterationVar), Loc, Loc); - if (IterationVarStmt.isInvalid() || IterationVar->isInvalidDecl()) + // auto b = begin-expr; + // auto e = end-expr; + ForRangeBeginEndInfo Info = BuildCXXForRangeBeginEndVars( + getCurScope(), Iterating->getRangeVar(), Loc, + /*CoawaitLoc=*/{}, + /*LifetimeExtendTemps=*/{}, BFRK_Build, /*Constexpr=*/false); + if (!Info.isValid()) return std::nullopt; - // i != end - DeclRefExpr *IterationVarDeclRef = BuildDeclRefExpr( - IterationVar, IterationVar->getType().getNonReferenceType(), VK_LValue, - Loc); - DeclRefExpr *End = BuildDeclRefExpr( - Iterating->getEndVar(), - Iterating->getEndVar()->getType().getNonReferenceType(), VK_LValue, - Loc); - ExprResult NotEqual = ActOnBinOp(getCurScope(), Loc, tok::exclaimequal, - IterationVarDeclRef, End); + StmtResult BeginStmt = + ActOnDeclStmt(ConvertDeclToDeclGroup(Info.BeginVar), Loc, Loc); + StmtResult EndStmt = + ActOnDeclStmt(ConvertDeclToDeclGroup(Info.EndVar), Loc, Loc); + if (BeginStmt.isInvalid() || EndStmt.isInvalid()) + return std::nullopt; + + // b != e + auto GetDeclRef = [&](VarDecl *VD) -> DeclRefExpr * { + return BuildDeclRefExpr(VD, VD->getType().getNonReferenceType(), + VK_LValue, Loc); + }; + + DeclRefExpr *Begin = GetDeclRef(Info.BeginVar); + DeclRefExpr *End = GetDeclRef(Info.EndVar); + ExprResult NotEqual = + ActOnBinOp(getCurScope(), Loc, tok::exclaimequal, Begin, End); if (NotEqual.isInvalid()) return std::nullopt; ConditionResult Condition = ActOnCondition( @@ -565,12 +587,10 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { if (Condition.isInvalid()) return std::nullopt; - // ++i - IterationVarDeclRef = BuildDeclRefExpr( - IterationVar, IterationVar->getType().getNonReferenceType(), VK_LValue, - Loc); + // ++b + Begin = GetDeclRef(Info.BeginVar); ExprResult Increment = - ActOnUnaryOp(getCurScope(), Loc, tok::plusplus, IterationVarDeclRef); + ActOnUnaryOp(getCurScope(), Loc, tok::plusplus, Begin); if (Increment.isInvalid()) return std::nullopt; FullExprArg ThirdPart = MakeFullDiscardedValueExpr(Increment.get()); @@ -593,9 +613,8 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { // Exit the for loop. InnerScope.Exit(); ForScope.Exit(); - StmtResult ForLoop = - ActOnForStmt(Loc, Loc, IterationVarStmt.get(), Condition, ThirdPart, - Loc, IncrementStmt.get()); + StmtResult ForLoop = ActOnForStmt(Loc, Loc, /*First=*/nullptr, Condition, + ThirdPart, Loc, IncrementStmt.get()); if (ForLoop.isInvalid()) return std::nullopt; @@ -607,9 +626,11 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { return std::nullopt; // Finally, we can build the compound statement that is the lambda body. - StmtResult LambdaBody = ActOnCompoundStmt( - Loc, Loc, {ResultVarStmt.get(), ForLoop.get(), Return.get()}, - /*isStmtExpr=*/false); + StmtResult LambdaBody = + ActOnCompoundStmt(Loc, Loc, + {ResultVarStmt.get(), BeginStmt.get(), EndStmt.get(), + ForLoop.get(), Return.get()}, + /*isStmtExpr=*/false); if (LambdaBody.isInvalid()) return std::nullopt; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 5a4c2ee9dd06f..f398fa59d88d7 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2410,10 +2410,10 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E, /// Build a variable declaration for a for-range statement. VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, QualType Type, - StringRef Name, bool ForExpansionStmt) { + StringRef Name, bool Constexpr) { // Making the variable constexpr doesn't automatically add 'const' to the // type, so do that now. - if (ForExpansionStmt && !Type->isReferenceType()) + if (Constexpr && !Type->isReferenceType()) Type = Type.withConst(); DeclContext *DC = SemaRef.CurContext; @@ -2423,7 +2423,7 @@ VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, QualType Type, TInfo, SC_None); Decl->setImplicit(); Decl->setCXXForRangeImplicitVar(true); - if (ForExpansionStmt) + if (Constexpr) // CWG 3044: Do not make the variable 'static'. Decl->setConstexpr(true); return Decl; @@ -2742,7 +2742,17 @@ Sema::ForRangeBeginEndInfo Sema::BuildCXXForRangeBeginEndVars( // // CWG 3043 – Do not apply lifetime extension to iterating // expansion statements. - if (getLangOpts().CPlusPlus23 && !ForExpansionStmt) + // + // Note: CWG 3131 makes it so the 'range' variable need not be + // constexpr anymore, which means that we probably *do* want + // lifetime extension in that case after all, contrary to what + // CWG 3043 currently states. This just works out naturally with + // this implementation at the moment, but wg21 insist on no lifetime + // extension for iterating expansion statements, then this instead + // needs to check whether we're building this for an expansion statement + // instead of just applying lifetime extension if the variable isn't + // constexpr (or we could pass in an empty range for 'LifetimeExtendTemps'). + if (getLangOpts().CPlusPlus23 && !Constexpr) ApplyForRangeOrExpansionStatementLifetimeExtension(RangeVar, LifetimeExtendTemps); From a1973c89dfbc35acfb8b4e6197b840123e0a4827 Mon Sep 17 00:00:00 2001 From: Sirraide Date: Wed, 3 Dec 2025 20:32:53 +0100 Subject: [PATCH 8/9] Add code that was accidentally deleted in rebase --- clang/lib/Sema/TreeTransform.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 4d85bc4f76c72..58ba5fadd6f08 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9339,6 +9339,21 @@ StmtResult TreeTransform::TransformCXXExpansionStmtPattern( SemaRef.Context, NewESD, Init, ExpansionVarStmt, Range.getAs(), Begin.getAs(), End.getAs(), 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=*/{}); + + if (Res.isInvalid()) + return StmtError(); + + NewPattern = cast(Res.get()); } else { llvm_unreachable("TODO"); } From 2731a8154441150cd72392331701622493640e3b Mon Sep 17 00:00:00 2001 From: Sirraide Date: Wed, 3 Dec 2025 20:43:08 +0100 Subject: [PATCH 9/9] Merge all pattern kinds into a single AST node --- clang/lib/Sema/SemaExpand.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index 13d981d7b426a..40891e96e97de 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -333,9 +333,9 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( if (ExpansionInitializer->isTypeDependent()) { ActOnDependentForRangeInitializer(ExpansionVar, BFRK_Build); - return new (Context) CXXDependentExpansionStmtPattern( - ESD, Init, ExpansionVarStmt, ExpansionInitializer, LParenLoc, ColonLoc, - RParenLoc); + return CXXExpansionStmtPattern::CreateDependent( + Context, ESD, Init, ExpansionVarStmt, ExpansionInitializer, LParenLoc, + ColonLoc, RParenLoc); } if (RequireCompleteType(ExpansionInitializer->getExprLoc(), @@ -363,8 +363,8 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( if (FinaliseExpansionVar(*this, ExpansionVar, Data.Initializer)) return StmtError(); - return new (Context) CXXIteratingExpansionStmtPattern( - ESD, Init, ExpansionVarStmt, Data.RangeDecl, Data.BeginDecl, + return CXXExpansionStmtPattern::CreateIterating( + Context, ESD, Init, ExpansionVarStmt, Data.RangeDecl, Data.BeginDecl, Data.EndDecl, LParenLoc, ColonLoc, RParenLoc); } @@ -392,8 +392,7 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { // Even if the size isn't technically dependent, delay expansion until // we're no longer in a template if this is an iterating expansion statement // since evaluating a lambda declared in a template doesn't work too well. - if (CurContext->isDependentContext() && - isa(Expansion)) + if (CurContext->isDependentContext() && Expansion->isIterating()) return Expansion; // This can fail if this is an iterating expansion statement. @@ -491,7 +490,7 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { // for (; b != e; ++b) ++result; // return result; // }() - if (auto *Iterating = dyn_cast(Expansion)) { + if (Expansion->isIterating()) { SourceLocation Loc = Expansion->getColonLoc(); EnterExpressionEvaluationContext ExprEvalCtx( *this, ExpressionEvaluationContext::ConstantEvaluated); @@ -556,7 +555,7 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { // auto b = begin-expr; // auto e = end-expr; ForRangeBeginEndInfo Info = BuildCXXForRangeBeginEndVars( - getCurScope(), Iterating->getRangeVar(), Loc, + getCurScope(), Expansion->getRangeVar(), Loc, /*CoawaitLoc=*/{}, /*LifetimeExtendTemps=*/{}, BFRK_Build, /*Constexpr=*/false); if (!Info.isValid())