2424using namespace clang ;
2525using namespace sema ;
2626
27+ namespace {
28+ struct IterableExpansionStmtData {
29+ enum class State {
30+ NotIterable,
31+ Error,
32+ Ok,
33+ };
34+
35+ DeclStmt *RangeDecl = nullptr ;
36+ DeclStmt *BeginDecl = nullptr ;
37+ DeclStmt *EndDecl = nullptr ;
38+ Expr *Initializer = nullptr ;
39+ State TheState = State::NotIterable;
40+
41+ bool isIterable () const { return TheState == State::Ok; }
42+ bool hasError () { return TheState == State::Error; }
43+ };
44+ } // namespace
45+
2746// Build a 'DeclRefExpr' designating the template parameter '__N'.
2847static DeclRefExpr *BuildIndexDRE (Sema &S, CXXExpansionStmtDecl *ESD) {
2948 return S.BuildDeclRefExpr (ESD->getIndexTemplateParm (),
@@ -42,6 +61,118 @@ static bool FinaliseExpansionVar(Sema &S, VarDecl *ExpansionVar,
4261 return ExpansionVar->isInvalidDecl ();
4362}
4463
64+ static IterableExpansionStmtData
65+ TryBuildIterableExpansionStmtInitializer (Sema &S, Expr *ExpansionInitializer,
66+ Expr *Index, SourceLocation ColonLoc,
67+ bool VarIsConstexpr) {
68+ IterableExpansionStmtData Data;
69+
70+ // C++26 [stmt.expand]p3: An expression is expansion-iterable if it does not
71+ // have array type [...]
72+ QualType Ty = ExpansionInitializer->getType ().getNonReferenceType ();
73+ if (Ty->isArrayType ())
74+ return Data;
75+
76+ // Lookup member and ADL 'begin()'/'end()'. Only check if they exist; even if
77+ // they're deleted, inaccessible, etc., this is still an iterating expansion
78+ // statement, albeit an ill-formed one.
79+ DeclarationNameInfo BeginName (&S.PP .getIdentifierTable ().get (" begin" ),
80+ ColonLoc);
81+ DeclarationNameInfo EndName (&S.PP .getIdentifierTable ().get (" end" ), ColonLoc);
82+
83+ // Try member lookup first.
84+ bool FoundBeginEnd = false ;
85+ if (auto *Record = Ty->getAsCXXRecordDecl ()) {
86+ LookupResult BeginLR (S, BeginName, Sema::LookupMemberName);
87+ LookupResult EndLR (S, EndName, Sema::LookupMemberName);
88+ FoundBeginEnd = S.LookupQualifiedName (BeginLR, Record) &&
89+ S.LookupQualifiedName (EndLR, Record);
90+ }
91+
92+ // Try ADL.
93+ //
94+ // If overload resolution for 'begin()' *and* 'end()' succeeds (irrespective
95+ // of whether it results in a usable candidate), then assume this is an
96+ // iterating expansion statement.
97+ auto HasADLCandidate = [&](DeclarationName Name) {
98+ OverloadCandidateSet Candidates (ColonLoc, OverloadCandidateSet::CSK_Normal);
99+ OverloadCandidateSet::iterator Best;
100+
101+ S.AddArgumentDependentLookupCandidates (Name, ColonLoc, ExpansionInitializer,
102+ /* ExplicitTemplateArgs=*/ nullptr ,
103+ Candidates);
104+
105+ return Candidates.BestViableFunction (S, ColonLoc, Best) !=
106+ OR_No_Viable_Function;
107+ };
108+
109+ if (!FoundBeginEnd && (!HasADLCandidate (BeginName.getName ()) ||
110+ !HasADLCandidate (EndName.getName ())))
111+ return Data;
112+
113+ auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
114+ if (VarIsConstexpr)
115+ Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
116+ EnterExpressionEvaluationContext ExprEvalCtx (S, Ctx);
117+
118+ // The declarations should be attached to the parent decl context.
119+ Sema::ContextRAII CtxGuard (
120+ S, S.CurContext ->getEnclosingNonExpansionStatementContext (),
121+ /* NewThis=*/ false );
122+
123+ // Ok, we know that this is supposed to be an iterable expansion statement;
124+ // delegate to the for-range code to build the range/begin/end variables.
125+ //
126+ // Any failure at this point is a hard error.
127+ Data.TheState = IterableExpansionStmtData::State::Error;
128+ Scope *Scope = S.getCurScope ();
129+
130+ // TODO: CWG 3131 changes how this range is declared.
131+ StmtResult Var = S.BuildCXXForRangeRangeVar (Scope, ExpansionInitializer,
132+ /* ForExpansionStmt=*/ true );
133+ if (Var.isInvalid ())
134+ return Data;
135+
136+ auto *RangeVar = cast<DeclStmt>(Var.get ());
137+ Sema::ForRangeBeginEndInfo Info = S.BuildCXXForRangeBeginEndVars (
138+ Scope, cast<VarDecl>(RangeVar->getSingleDecl ()), ColonLoc,
139+ /* CoawaitLoc=*/ {},
140+ /* LifetimeExtendTemps=*/ {}, Sema::BFRK_Build, /* ForExpansionStmt=*/ true );
141+
142+ if (!Info.isValid ())
143+ return Data;
144+
145+ StmtResult BeginStmt = S.ActOnDeclStmt (
146+ S.ConvertDeclToDeclGroup (Info.BeginVar ), ColonLoc, ColonLoc);
147+ StmtResult EndStmt = S.ActOnDeclStmt (S.ConvertDeclToDeclGroup (Info.EndVar ),
148+ ColonLoc, ColonLoc);
149+ if (BeginStmt.isInvalid () || EndStmt.isInvalid ())
150+ return Data;
151+
152+ // Build '*(begin + i)'.
153+ DeclRefExpr *Begin = S.BuildDeclRefExpr (
154+ Info.BeginVar , Info.BeginVar ->getType ().getNonReferenceType (), VK_LValue,
155+ ColonLoc);
156+
157+ ExprResult BeginPlusI =
158+ S.ActOnBinOp (Scope, ColonLoc, tok::plus, Begin, Index);
159+ if (BeginPlusI.isInvalid ())
160+ return Data;
161+
162+ ExprResult Deref =
163+ S.ActOnUnaryOp (Scope, ColonLoc, tok::star, BeginPlusI.get ());
164+ if (Deref.isInvalid ())
165+ return Data;
166+
167+ Deref = S.MaybeCreateExprWithCleanups (Deref.get ());
168+ Data.BeginDecl = BeginStmt.getAs <DeclStmt>();
169+ Data.EndDecl = EndStmt.getAs <DeclStmt>();
170+ Data.RangeDecl = RangeVar;
171+ Data.Initializer = Deref.get ();
172+ Data.TheState = IterableExpansionStmtData::State::Ok;
173+ return Data;
174+ }
175+
45176CXXExpansionStmtDecl *
46177Sema::ActOnCXXExpansionStmtDecl (unsigned TemplateDepth,
47178 SourceLocation TemplateKWLoc) {
@@ -115,8 +246,26 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern(
115246 ColonLoc, RParenLoc);
116247 }
117248
118- Diag (ESD->getLocation (), diag::err_expansion_statements_todo);
119- return StmtError ();
249+ if (ExpansionInitializer->hasPlaceholderType ()) {
250+ ExprResult R = CheckPlaceholderExpr (ExpansionInitializer);
251+ if (R.isInvalid ())
252+ return StmtError ();
253+ ExpansionInitializer = R.get ();
254+ }
255+
256+ if (DiagnoseUnexpandedParameterPack (ExpansionInitializer))
257+ return StmtError ();
258+
259+ // Reject lambdas early.
260+ if (auto *RD = ExpansionInitializer->getType ()->getAsCXXRecordDecl ();
261+ RD && RD->isLambda ()) {
262+ Diag (ExpansionInitializer->getBeginLoc (), diag::err_expansion_stmt_lambda);
263+ return StmtError ();
264+ }
265+
266+ return BuildNonEnumeratingCXXExpansionStmtPattern (
267+ ESD, Init, DS, ExpansionInitializer, LParenLoc, ColonLoc, RParenLoc,
268+ LifetimeExtendTemps);
120269}
121270
122271StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern (
@@ -127,6 +276,43 @@ StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern(
127276 LParenLoc, ColonLoc, RParenLoc);
128277}
129278
279+ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern (
280+ CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt,
281+ Expr *ExpansionInitializer, SourceLocation LParenLoc,
282+ SourceLocation ColonLoc, SourceLocation RParenLoc,
283+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
284+ VarDecl *ExpansionVar = cast<VarDecl>(ExpansionVarStmt->getSingleDecl ());
285+
286+ if (ExpansionInitializer->isTypeDependent ()) {
287+ ActOnDependentForRangeInitializer (ExpansionVar, BFRK_Build);
288+ return new (Context) CXXDependentExpansionStmtPattern (
289+ ESD, Init, ExpansionVarStmt, ExpansionInitializer, LParenLoc, ColonLoc,
290+ RParenLoc);
291+ }
292+
293+ // Otherwise, if it can be an iterating expansion statement, it is one.
294+ DeclRefExpr *Index = BuildIndexDRE (*this , ESD);
295+ IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer (
296+ *this , ExpansionInitializer, Index, ColonLoc,
297+ ExpansionVar->isConstexpr ());
298+ if (Data.hasError ()) {
299+ ActOnInitializerError (ExpansionVar);
300+ return StmtError ();
301+ }
302+
303+ if (Data.isIterable ()) {
304+ if (FinaliseExpansionVar (*this , ExpansionVar, Data.Initializer ))
305+ return StmtError ();
306+
307+ return new (Context) CXXIteratingExpansionStmtPattern (
308+ ESD, Init, ExpansionVarStmt, Data.RangeDecl , Data.BeginDecl ,
309+ Data.EndDecl , LParenLoc, ColonLoc, RParenLoc);
310+ }
311+
312+ Diag (ESD->getLocation (), diag::err_expansion_statements_todo);
313+ return StmtError ();
314+ }
315+
130316StmtResult Sema::FinishCXXExpansionStmt (Stmt *Exp, Stmt *Body) {
131317 if (!Exp || !Body)
132318 return StmtError ();
@@ -149,7 +335,13 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
149335 if (Expansion->getInit ())
150336 Shared.push_back (Expansion->getInit ());
151337
152- assert (isa<CXXEnumeratingExpansionStmtPattern>(Expansion) && " TODO" );
338+ if (auto *Iter = dyn_cast<CXXIteratingExpansionStmtPattern>(Expansion)) {
339+ Shared.push_back (Iter->getRangeVarStmt ());
340+ Shared.push_back (Iter->getBeginVarStmt ());
341+ Shared.push_back (Iter->getEndVarStmt ());
342+ } else {
343+ assert (isa<CXXEnumeratingExpansionStmtPattern>(Expansion) && " TODO" );
344+ }
153345
154346 // Return an empty statement if the range is empty.
155347 if (*NumInstantiations == 0 ) {
@@ -228,5 +420,53 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
228420 ->getExprs ()
229421 .size ();
230422
423+ // By [stmt.expand]5.2, N is the result of evaluating the expression
424+ //
425+ // [] consteval {
426+ // std::ptrdiff_t result = 0;
427+ // for (auto i = begin; i != end; ++i) ++result;
428+ // return result;
429+ // }()
430+ // TODO: CWG 3131 changes this lambda a bit.
431+ if (auto *Iterating = dyn_cast<CXXIteratingExpansionStmtPattern>(Expansion)) {
432+ EnterExpressionEvaluationContext ExprEvalCtx (
433+ *this , ExpressionEvaluationContext::ConstantEvaluated);
434+
435+ // FIXME: Actually do that; unfortunately, conjuring a lambda out of thin
436+ // air in Sema is a massive pain, so for now just cheat by computing
437+ // 'end - begin'.
438+ SourceLocation Loc = Iterating->getColonLoc ();
439+ DeclRefExpr *Begin = BuildDeclRefExpr (
440+ Iterating->getBeginVar (),
441+ Iterating->getBeginVar ()->getType ().getNonReferenceType (), VK_LValue,
442+ Loc);
443+
444+ DeclRefExpr *End = BuildDeclRefExpr (
445+ Iterating->getEndVar (),
446+ Iterating->getEndVar ()->getType ().getNonReferenceType (), VK_LValue,
447+ Loc);
448+
449+ ExprResult N = ActOnBinOp (getCurScope (), Loc, tok::minus, End, Begin);
450+ if (N.isInvalid ())
451+ return std::nullopt ;
452+
453+ Expr::EvalResult ER;
454+ SmallVector<PartialDiagnosticAt, 4 > Notes;
455+ ER.Diag = &Notes;
456+ if (!N.get ()->EvaluateAsInt (ER, Context)) {
457+ Diag (Loc, diag::err_expansion_size_expr_not_ice);
458+ for (const auto &[Location, PDiag] : Notes)
459+ Diag (Location, PDiag);
460+ return std::nullopt ;
461+ }
462+
463+ if (ER.Val .getInt ().isNegative ()) {
464+ Diag (Loc, diag::err_expansion_size_negative) << ER.Val .getInt ();
465+ return std::nullopt ;
466+ }
467+
468+ return ER.Val .getInt ().getZExtValue ();
469+ }
470+
231471 llvm_unreachable (" TODO" );
232472}
0 commit comments