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) {
@@ -112,8 +243,26 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern(
112243 ColonLoc, RParenLoc);
113244 }
114245
115- Diag (ESD->getLocation (), diag::err_expansion_statements_todo);
116- return StmtError ();
246+ if (ExpansionInitializer->hasPlaceholderType ()) {
247+ ExprResult R = CheckPlaceholderExpr (ExpansionInitializer);
248+ if (R.isInvalid ())
249+ return StmtError ();
250+ ExpansionInitializer = R.get ();
251+ }
252+
253+ if (DiagnoseUnexpandedParameterPack (ExpansionInitializer))
254+ return StmtError ();
255+
256+ // Reject lambdas early.
257+ if (auto *RD = ExpansionInitializer->getType ()->getAsCXXRecordDecl ();
258+ RD && RD->isLambda ()) {
259+ Diag (ExpansionInitializer->getBeginLoc (), diag::err_expansion_stmt_lambda);
260+ return StmtError ();
261+ }
262+
263+ return BuildNonEnumeratingCXXExpansionStmtPattern (
264+ ESD, Init, DS, ExpansionInitializer, LParenLoc, ColonLoc, RParenLoc,
265+ LifetimeExtendTemps);
117266}
118267
119268StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern (
@@ -124,6 +273,43 @@ StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern(
124273 LParenLoc, ColonLoc, RParenLoc);
125274}
126275
276+ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern (
277+ CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt,
278+ Expr *ExpansionInitializer, SourceLocation LParenLoc,
279+ SourceLocation ColonLoc, SourceLocation RParenLoc,
280+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
281+ VarDecl *ExpansionVar = cast<VarDecl>(ExpansionVarStmt->getSingleDecl ());
282+
283+ if (ExpansionInitializer->isTypeDependent ()) {
284+ ActOnDependentForRangeInitializer (ExpansionVar, BFRK_Build);
285+ return new (Context) CXXDependentExpansionStmtPattern (
286+ ESD, Init, ExpansionVarStmt, ExpansionInitializer, LParenLoc, ColonLoc,
287+ RParenLoc);
288+ }
289+
290+ // Otherwise, if it can be an iterating expansion statement, it is one.
291+ DeclRefExpr *Index = BuildIndexDRE (*this , ESD);
292+ IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer (
293+ *this , ExpansionInitializer, Index, ColonLoc,
294+ ExpansionVar->isConstexpr ());
295+ if (Data.hasError ()) {
296+ ActOnInitializerError (ExpansionVar);
297+ return StmtError ();
298+ }
299+
300+ if (Data.isIterable ()) {
301+ if (FinaliseExpansionVar (*this , ExpansionVar, Data.Initializer ))
302+ return StmtError ();
303+
304+ return new (Context) CXXIteratingExpansionStmtPattern (
305+ ESD, Init, ExpansionVarStmt, Data.RangeDecl , Data.BeginDecl ,
306+ Data.EndDecl , LParenLoc, ColonLoc, RParenLoc);
307+ }
308+
309+ Diag (ESD->getLocation (), diag::err_expansion_statements_todo);
310+ return StmtError ();
311+ }
312+
127313StmtResult Sema::FinishCXXExpansionStmt (Stmt *Exp, Stmt *Body) {
128314 if (!Exp || !Body)
129315 return StmtError ();
@@ -146,7 +332,13 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
146332 if (Expansion->getInit ())
147333 Shared.push_back (Expansion->getInit ());
148334
149- assert (isa<CXXEnumeratingExpansionStmtPattern>(Expansion) && " TODO" );
335+ if (auto *Iter = dyn_cast<CXXIteratingExpansionStmtPattern>(Expansion)) {
336+ Shared.push_back (Iter->getRangeVarStmt ());
337+ Shared.push_back (Iter->getBeginVarStmt ());
338+ Shared.push_back (Iter->getEndVarStmt ());
339+ } else {
340+ assert (isa<CXXEnumeratingExpansionStmtPattern>(Expansion) && " TODO" );
341+ }
150342
151343 // Return an empty statement if the range is empty.
152344 if (*NumInstantiations == 0 ) {
@@ -225,5 +417,53 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
225417 ->getExprs ()
226418 .size ();
227419
420+ // By [stmt.expand]5.2, N is the result of evaluating the expression
421+ //
422+ // [] consteval {
423+ // std::ptrdiff_t result = 0;
424+ // for (auto i = begin; i != end; ++i) ++result;
425+ // return result;
426+ // }()
427+ // TODO: CWG 3131 changes this lambda a bit.
428+ if (auto *Iterating = dyn_cast<CXXIteratingExpansionStmtPattern>(Expansion)) {
429+ EnterExpressionEvaluationContext ExprEvalCtx (
430+ *this , ExpressionEvaluationContext::ConstantEvaluated);
431+
432+ // FIXME: Actually do that; unfortunately, conjuring a lambda out of thin
433+ // air in Sema is a massive pain, so for now just cheat by computing
434+ // 'end - begin'.
435+ SourceLocation Loc = Iterating->getColonLoc ();
436+ DeclRefExpr *Begin = BuildDeclRefExpr (
437+ Iterating->getBeginVar (),
438+ Iterating->getBeginVar ()->getType ().getNonReferenceType (), VK_LValue,
439+ Loc);
440+
441+ DeclRefExpr *End = BuildDeclRefExpr (
442+ Iterating->getEndVar (),
443+ Iterating->getEndVar ()->getType ().getNonReferenceType (), VK_LValue,
444+ Loc);
445+
446+ ExprResult N = ActOnBinOp (getCurScope (), Loc, tok::minus, End, Begin);
447+ if (N.isInvalid ())
448+ return std::nullopt ;
449+
450+ Expr::EvalResult ER;
451+ SmallVector<PartialDiagnosticAt, 4 > Notes;
452+ ER.Diag = &Notes;
453+ if (!N.get ()->EvaluateAsInt (ER, Context)) {
454+ Diag (Loc, diag::err_expansion_size_expr_not_ice);
455+ for (const auto &[Location, PDiag] : Notes)
456+ Diag (Location, PDiag);
457+ return std::nullopt ;
458+ }
459+
460+ if (ER.Val .getInt ().isNegative ()) {
461+ Diag (Loc, diag::err_expansion_size_negative) << ER.Val .getInt ();
462+ return std::nullopt ;
463+ }
464+
465+ return ER.Val .getInt ().getZExtValue ();
466+ }
467+
228468 llvm_unreachable (" TODO" );
229469}
0 commit comments