1//===-- SemaExpand.cpp - Semantic Analysis for Expansion Statements--------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements semantic analysis for C++26 expansion statements,
10// aka 'template for'.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/AST/DeclCXX.h"
15#include "clang/AST/ExprCXX.h"
16#include "clang/AST/StmtCXX.h"
17#include "clang/Lex/Preprocessor.h"
18#include "clang/Sema/EnterExpressionEvaluationContext.h"
19#include "clang/Sema/Lookup.h"
20#include "clang/Sema/Overload.h"
21#include "clang/Sema/Sema.h"
22#include "clang/Sema/Template.h"
23#include "llvm/ADT/ScopeExit.h"
24
25using namespace clang;
26
27namespace {
28struct IterableExpansionStmtData {
29 /// When we try to determine whether an expansion statement is iterating, the
30 /// result can be
31 ///
32 /// 1. it is not iterating (it could still be destructuring; we don't
33 /// know that yet);
34 ///
35 /// 2. there was a hard error (e.g. we determined that it is iterating,
36 /// but begin() is deleted);
37 ///
38 /// 3. it is iterating, and there was no error.
39 enum class IsIterableResult {
40 NotIterable, ///< Not iterable, but also not invalid.
41 Error, ///< Ill-formed.
42 Iterable, ///< Iterable (and there was no error).
43 };
44
45 DeclStmt *RangeDecl = nullptr;
46 DeclStmt *BeginDecl = nullptr;
47 DeclStmt *IterDecl = nullptr;
48 IsIterableResult TheState = IsIterableResult::NotIterable;
49
50 bool isIterable() const { return TheState == IsIterableResult::Iterable; }
51 bool hasError() { return TheState == IsIterableResult::Error; }
52};
53} // namespace
54
55// Build a 'DeclRefExpr' designating the template parameter that is used as
56// the expansion index
57static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) {
58 return S.BuildDeclRefExpr(D: ESD->getIndexTemplateParm(),
59 Ty: S.Context.getPointerDiffType(), VK: VK_PRValue,
60 Loc: ESD->getBeginLoc());
61}
62
63static bool FinalizeExpansionVar(Sema &S, VarDecl *ExpansionVar,
64 ExprResult Initializer) {
65 if (Initializer.isInvalid()) {
66 S.ActOnInitializerError(Dcl: ExpansionVar);
67 return true;
68 }
69
70 S.AddInitializerToDecl(dcl: ExpansionVar, init: Initializer.get(), /*DirectInit=*/false);
71 return ExpansionVar->isInvalidDecl();
72}
73
74static auto InitListContainsPack(const InitListExpr *ILE) {
75 return llvm::any_of(Range: ILE->inits(),
76 P: [](const Expr *E) { return isa<PackExpansionExpr>(Val: E); });
77}
78
79static bool HasDependentSize(const DeclContext *CurContext,
80 const CXXExpansionStmtPattern *Pattern) {
81 switch (Pattern->getKind()) {
82 case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: {
83 auto *SelectExpr = cast<CXXExpansionSelectExpr>(
84 Val: Pattern->getExpansionVariable()->getInit());
85 return InitListContainsPack(ILE: SelectExpr->getRangeExpr());
86 }
87
88 case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
89 // Even if the size isn't technically dependent, delay expansion until
90 // we're no longer in a template since evaluating a lambda declared in
91 // a template doesn't work too well.
92 assert(CurContext->isExpansionStmt());
93 return CurContext->getParent()->isDependentContext();
94
95 case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent:
96 return true;
97
98 case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
99 return false;
100 }
101
102 llvm_unreachable("invalid pattern kind");
103}
104
105static IterableExpansionStmtData TryBuildIterableExpansionStmtInitializer(
106 Sema &S, Expr *ExpansionInitializer, Expr *Index, SourceLocation ColonLoc,
107 bool VarIsConstexpr,
108 ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
109 IterableExpansionStmtData Data;
110
111 // C++26 [stmt.expand]p3: An expression is expansion-iterable if it does not
112 // have array type [...]
113 QualType Ty = ExpansionInitializer->getType().getNonReferenceType();
114 if (Ty->isArrayType())
115 return Data;
116
117 // Lookup member and ADL 'begin()'/'end()'. Only check if they exist; even if
118 // they're deleted, inaccessible, etc., this is still an iterating expansion
119 // statement, albeit an ill-formed one.
120 DeclarationNameInfo BeginName(&S.PP.getIdentifierTable().get(Name: "begin"),
121 ColonLoc);
122 DeclarationNameInfo EndName(&S.PP.getIdentifierTable().get(Name: "end"), ColonLoc);
123
124 bool FoundBeginEnd = false;
125 if (auto *Record = Ty->getAsCXXRecordDecl()) {
126 LookupResult BeginLR(S, BeginName, Sema::LookupMemberName);
127 LookupResult EndLR(S, EndName, Sema::LookupMemberName);
128 FoundBeginEnd = S.LookupQualifiedName(R&: BeginLR, LookupCtx: Record) &&
129 S.LookupQualifiedName(R&: EndLR, LookupCtx: Record);
130 }
131
132 // If member lookup doesn't yield anything, try ADL.
133 //
134 // If overload resolution for 'begin()' *and* 'end()' succeeds (irrespective
135 // of whether it results in a usable candidate), then assume this is an
136 // iterating expansion statement.
137 auto HasADLCandidate = [&](DeclarationName Name) {
138 OverloadCandidateSet Candidates(ColonLoc, OverloadCandidateSet::CSK_Normal);
139 OverloadCandidateSet::iterator Best;
140
141 S.AddArgumentDependentLookupCandidates(Name, Loc: ColonLoc, Args: ExpansionInitializer,
142 /*ExplicitTemplateArgs=*/nullptr,
143 CandidateSet&: Candidates);
144
145 return Candidates.BestViableFunction(S, Loc: ColonLoc, Best) !=
146 OR_No_Viable_Function;
147 };
148
149 if (!FoundBeginEnd && (!HasADLCandidate(BeginName.getName()) ||
150 !HasADLCandidate(EndName.getName())))
151 return Data;
152
153 auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
154 if (VarIsConstexpr)
155 Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
156 EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx);
157
158 // The declarations should be attached to the parent decl context.
159 Sema::ContextRAII CtxGuard(S, S.CurContext->getParent(),
160 /*NewThis=*/false);
161
162 // We know that this is supposed to be an iterable expansion statement;
163 // delegate to the for-range code to build the range/begin/end variables.
164 //
165 // Any failure at this point is a hard error.
166 Data.TheState = IterableExpansionStmtData::IsIterableResult::Error;
167 Scope *Scope = S.getCurScope();
168
169 // By [stmt.expand]p5.2 (CWG3131), the declaration of 'range' is of the form
170 //
171 // constexpr[opt] decltype(auto) range = (expansion-initializer);
172 //
173 // where 'constexpr' is present iff the for-range-declaration is 'constexpr'.
174 StmtResult Var = S.BuildCXXForRangeRangeVar(
175 S: Scope, Range: S.ActOnParenExpr(L: ColonLoc, R: ColonLoc, E: ExpansionInitializer).get(),
176 Type: S.Context.getAutoType(DK: DeducedKind::Undeduced, DeducedAsType: QualType(),
177 Keyword: AutoTypeKeyword::DecltypeAuto),
178 IsConstexpr: VarIsConstexpr);
179 if (Var.isInvalid())
180 return Data;
181
182 // [stmt.expand]p5.2 (CWG3140): The keyword 'constexpr' is present in the
183 // declarations of 'range', 'begin', and 'iter' if and only if 'constexpr' is
184 // one of the decl-specifiers of the decl-specifier-seq of the
185 // for-range-declaration.
186 //
187 // FIXME: As of CWG3140, we should only create 'begin' here, and not 'end',
188 // but that requires another substantial refactor of the for-range code.
189 auto *RangeVar = cast<DeclStmt>(Val: Var.get());
190 Sema::ForRangeBeginEndInfo Info = S.BuildCXXForRangeBeginEndVars(
191 S: Scope, RangeVar: cast<VarDecl>(Val: RangeVar->getSingleDecl()), ColonLoc,
192 /*CoawaitLoc=*/{},
193 /*LifetimeExtendTemps=*/{}, Kind: Sema::BFRK_Build, IsConstexpr: VarIsConstexpr);
194
195 if (!Info.isValid())
196 return Data;
197
198 // At runtime, we only need to evaluate 'begin', whereas 'end' is only used at
199 // compile-time; we'll rebuild the latter when we compute the expansion size,
200 // so only build a DeclStmt for 'begin' here.
201 StmtResult BeginStmt = S.ActOnDeclStmt(
202 Decl: S.ConvertDeclToDeclGroup(Ptr: Info.BeginVar), StartLoc: ColonLoc, EndLoc: ColonLoc);
203 if (BeginStmt.isInvalid())
204 return Data;
205
206 // TODO: Build 'constexpr auto iter = begin + decltype(begin - begin){i};'.
207 S.Diag(Loc: ColonLoc, DiagID: diag::err_iterating_expansion_stmt_unsupported);
208 return Data;
209
210#if 0 // This will be used once we support iterating expansion statements.
211 // Store it in a variable.
212 // See also Sema::BuildCXXForRangeBeginEndVars().
213 const auto DepthStr = std::to_string(Scope->getDepth() / 2);
214 IdentifierInfo *Name =
215 S.PP.getIdentifierInfo(std::string("__iter") + DepthStr);
216 VarDecl *IterVar = S.BuildForRangeVarDecl(
217 ColonLoc, S.Context.getAutoDeductType(), Name, VarIsConstexpr);
218 S.AddInitializerToDecl(IterVar, BeginPlusI.get(), /*DirectInit=*/false);
219 if (IterVar->isInvalidDecl())
220 return Data;
221
222 StmtResult IterVarStmt =
223 S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(IterVar), ColonLoc, ColonLoc);
224 if (IterVarStmt.isInvalid())
225 return Data;
226
227 // CWG 3149: Apply lifetime extension to iterating expansion statements.
228 S.ApplyForRangeOrExpansionStatementLifetimeExtension(
229 cast<VarDecl>(RangeVar->getSingleDecl()), LifetimeExtendTemps);
230
231 Data.BeginDecl = BeginStmt.getAs<DeclStmt>();
232 Data.RangeDecl = RangeVar;
233 Data.IterDecl = IterVarStmt.getAs<DeclStmt>();
234 Data.TheState = IterableExpansionStmtData::IsIterableResult::Iterable;
235 return Data;
236#endif
237}
238
239static StmtResult BuildDestructuringDecompositionDecl(
240 Sema &S, Expr *ExpansionInitializer, SourceLocation ColonLoc,
241 bool VarIsConstexpr,
242 ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
243 auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
244 if (VarIsConstexpr)
245 Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
246 EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx);
247
248 // The declarations should be attached to the parent decl context.
249 Sema::ContextRAII CtxGuard(S, S.CurContext->getParent(),
250 /*NewThis=*/false);
251
252 UnsignedOrNone Arity =
253 S.GetDecompositionElementCount(DecompType: ExpansionInitializer->getType(), Loc: ColonLoc);
254
255 if (!Arity)
256 return StmtError();
257
258 QualType AutoRRef = S.Context.getAutoRRefDeductType();
259 SmallVector<BindingDecl *> Bindings;
260 for (unsigned I = 0; I < *Arity; ++I)
261 Bindings.push_back(Elt: BindingDecl::Create(
262 C&: S.Context, DC: S.CurContext, IdLoc: ColonLoc,
263 Id: S.getPreprocessor().getIdentifierInfo(Name: "__u" + std::to_string(val: I)),
264 T: AutoRRef));
265
266 TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(T: AutoRRef);
267 auto *DD =
268 DecompositionDecl::Create(C&: S.Context, DC: S.CurContext, StartLoc: ColonLoc, LSquareLoc: ColonLoc,
269 RSquareLoc: ColonLoc, T: AutoRRef, TInfo: TSI, S: SC_Auto, Bindings);
270
271 if (VarIsConstexpr)
272 DD->setConstexpr(true);
273
274 S.ApplyForRangeOrExpansionStatementLifetimeExtension(RangeVar: DD, Temporaries: LifetimeExtendTemps);
275 S.AddInitializerToDecl(dcl: DD, init: ExpansionInitializer, DirectInit: false);
276 return S.ActOnDeclStmt(Decl: S.ConvertDeclToDeclGroup(Ptr: DD), StartLoc: ColonLoc, EndLoc: ColonLoc);
277}
278
279CXXExpansionStmtDecl *
280Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
281 SourceLocation TemplateKWLoc) {
282 // Create a template parameter. This will be used to denote the index
283 // of the element that we're instantiating. This type must be 'ptrdiff_t'
284 // for iterating expansion statements ([stmt.expand]p5.2), so use that in
285 // all cases.
286 QualType ParmTy = Context.getPointerDiffType();
287 TypeSourceInfo *ParmTI =
288 Context.getTrivialTypeSourceInfo(T: ParmTy, Loc: TemplateKWLoc);
289
290 auto *TParam = NonTypeTemplateParmDecl::Create(
291 C: Context, DC: Context.getTranslationUnitDecl(), StartLoc: TemplateKWLoc, IdLoc: TemplateKWLoc,
292 D: TemplateDepth, /*Position=*/P: 0, /*Id=*/nullptr, T: ParmTy,
293 /*ParameterPack=*/false, TInfo: ParmTI);
294
295 return BuildCXXExpansionStmtDecl(Ctx: CurContext, TemplateKWLoc, NTTP: TParam);
296}
297
298CXXExpansionStmtDecl *
299Sema::BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc,
300 NonTypeTemplateParmDecl *NTTP) {
301 auto *Result =
302 CXXExpansionStmtDecl::Create(C&: Context, DC: Ctx, Loc: TemplateKWLoc, NTTP);
303 Ctx->addDecl(D: Result);
304 return Result;
305}
306
307ExprResult Sema::ActOnCXXExpansionInitList(MultiExprArg SubExprs,
308 SourceLocation LBraceLoc,
309 SourceLocation RBraceLoc) {
310 return new (Context) InitListExpr(Context, LBraceLoc, SubExprs, RBraceLoc,
311 /*IsExplicit=*/true);
312}
313
314StmtResult Sema::ActOnCXXExpansionStmtPattern(
315 CXXExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt,
316 Expr *ExpansionInitializer, SourceLocation LParenLoc,
317 SourceLocation ColonLoc, SourceLocation RParenLoc,
318 ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
319 if (!ExpansionInitializer || ExpansionInitializer->containsErrors() ||
320 !ExpansionVarStmt)
321 return StmtError();
322
323 assert(CurContext->isExpansionStmt());
324 auto *DS = cast<DeclStmt>(Val: ExpansionVarStmt);
325 if (!DS->isSingleDecl()) {
326 Diag(Loc: DS->getBeginLoc(), DiagID: diag::err_type_defined_in_for_range);
327 return StmtError();
328 }
329
330 VarDecl *ExpansionVar = dyn_cast<VarDecl>(Val: DS->getSingleDecl());
331 if (!ExpansionVar || ExpansionVar->isInvalidDecl())
332 return StmtError();
333
334 // This is an enumerating expansion statement.
335 if (auto *ILE = dyn_cast<InitListExpr>(Val: ExpansionInitializer)) {
336 assert(ILE->isSyntacticForm());
337 ExprResult Initializer =
338 BuildCXXExpansionSelectExpr(Range: ILE, Idx: BuildIndexDRE(S&: *this, ESD));
339 if (FinalizeExpansionVar(S&: *this, ExpansionVar, Initializer))
340 return StmtError();
341
342 // TODO: CWG3043 (lifetime extension in enumerating expansion statements).
343 return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, ExpansionVar: DS, LParenLoc,
344 ColonLoc, RParenLoc);
345 }
346
347 if (ExpansionInitializer->hasPlaceholderType()) {
348 ExprResult R = CheckPlaceholderExpr(E: ExpansionInitializer);
349 if (R.isInvalid())
350 return StmtError();
351 ExpansionInitializer = R.get();
352 }
353
354 if (DiagnoseUnexpandedParameterPack(E: ExpansionInitializer))
355 return StmtError();
356
357 return BuildNonEnumeratingCXXExpansionStmtPattern(
358 ESD, Init, ExpansionVarStmt: DS, ExpansionInitializer, LParenLoc, ColonLoc, RParenLoc,
359 LifetimeExtendTemps);
360}
361
362StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern(
363 Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc,
364 SourceLocation ColonLoc, SourceLocation RParenLoc) {
365 return CXXExpansionStmtPattern::CreateEnumerating(
366 Context, ESD: cast<CXXExpansionStmtDecl>(Val: ESD), Init,
367 ExpansionVar: cast<DeclStmt>(Val: ExpansionVar), LParenLoc, ColonLoc, RParenLoc);
368}
369
370StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
371 CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt,
372 Expr *ExpansionInitializer, SourceLocation LParenLoc,
373 SourceLocation ColonLoc, SourceLocation RParenLoc,
374 ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
375 VarDecl *ExpansionVar = cast<VarDecl>(Val: ExpansionVarStmt->getSingleDecl());
376
377 // Reject lambdas early.
378 if (auto *RD = ExpansionInitializer->getType()->getAsCXXRecordDecl();
379 RD && RD->isLambda()) {
380 Diag(Loc: ExpansionInitializer->getBeginLoc(), DiagID: diag::err_expansion_stmt_lambda);
381 return StmtError();
382 }
383
384 if (ExpansionInitializer->isTypeDependent()) {
385 ActOnDependentForRangeInitializer(LoopVar: ExpansionVar, BFRK: BFRK_Build);
386 return CXXExpansionStmtPattern::CreateDependent(
387 Context, ESD, Init, ExpansionVar: ExpansionVarStmt, ExpansionInitializer, LParenLoc,
388 ColonLoc, RParenLoc);
389 }
390
391 if (RequireCompleteType(Loc: ExpansionInitializer->getExprLoc(),
392 T: ExpansionInitializer->getType(),
393 DiagID: diag::err_expansion_stmt_incomplete))
394 return StmtError();
395
396 if (ExpansionInitializer->getType()->isVariableArrayType()) {
397 Diag(Loc: ExpansionInitializer->getExprLoc(), DiagID: diag::err_expansion_stmt_vla)
398 << ExpansionInitializer->getType();
399 return StmtError();
400 }
401
402 // Otherwise, if it can be an iterating expansion statement, it is one.
403 DeclRefExpr *Index = BuildIndexDRE(S&: *this, ESD);
404 IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer(
405 S&: *this, ExpansionInitializer, Index, ColonLoc, VarIsConstexpr: ExpansionVar->isConstexpr(),
406 LifetimeExtendTemps);
407 if (Data.hasError()) {
408 ActOnInitializerError(Dcl: ExpansionVar);
409 return StmtError();
410 }
411
412 if (Data.isIterable()) {
413 // Build '*iter'.
414 auto *IterVar = cast<VarDecl>(Val: Data.IterDecl->getSingleDecl());
415 DeclRefExpr *IterDRE = BuildDeclRefExpr(
416 D: IterVar, Ty: IterVar->getType().getNonReferenceType(), VK: VK_LValue, Loc: ColonLoc);
417 ExprResult Deref =
418 ActOnUnaryOp(S: getCurScope(), OpLoc: ColonLoc, Op: tok::star, Input: IterDRE);
419 if (Deref.isInvalid()) {
420 ActOnInitializerError(Dcl: ExpansionVar);
421 return StmtError();
422 }
423
424 Deref = MaybeCreateExprWithCleanups(SubExpr: Deref.get());
425
426 if (FinalizeExpansionVar(S&: *this, ExpansionVar, Initializer: Deref.get()))
427 return StmtError();
428
429 return CXXExpansionStmtPattern::CreateIterating(
430 Context, ESD, Init, ExpansionVar: ExpansionVarStmt, Range: Data.RangeDecl, Begin: Data.BeginDecl,
431 Iter: Data.IterDecl, LParenLoc, ColonLoc, RParenLoc);
432 }
433
434 // If not, try destructuring.
435 StmtResult DecompDeclStmt = BuildDestructuringDecompositionDecl(
436 S&: *this, ExpansionInitializer, ColonLoc, VarIsConstexpr: ExpansionVar->isConstexpr(),
437 LifetimeExtendTemps);
438 if (DecompDeclStmt.isInvalid()) {
439 Diag(Loc: ExpansionInitializer->getBeginLoc(),
440 DiagID: diag::err_expansion_stmt_invalid_init)
441 << ExpansionInitializer->getType()
442 << ExpansionInitializer->getSourceRange();
443
444 ActOnInitializerError(Dcl: ExpansionVar);
445 return StmtError();
446 }
447
448 auto *DS = DecompDeclStmt.getAs<DeclStmt>();
449 auto *DD = cast<DecompositionDecl>(Val: DS->getSingleDecl());
450 if (DD->isInvalidDecl())
451 return StmtError();
452
453 // Synthesise an InitListExpr to store the bindings; this essentially lets us
454 // desugar the expansion of a destructuring expansion statement to that of an
455 // enumerating expansion statement.
456 SmallVector<Expr *> Bindings;
457 Bindings.reserve(N: DD->bindings().size());
458 for (BindingDecl *BD : DD->bindings()) {
459 Expr *Element = BuildDeclRefExpr(D: BD, Ty: BD->getType().getNonReferenceType(),
460 VK: VK_LValue, Loc: ColonLoc);
461
462 // [stmt.expand]p5.3 (CWG3149): If the expansion-initializer is an lvalue,
463 // then vi is ui; otherwise, vi is static_cast<decltype(ui)&&>(ui).
464 if (!ExpansionInitializer->isLValue()) {
465 QualType Ty =
466 BuildReferenceType(T: getDecltypeForExpr(E: Element), /*LValueRef=*/false,
467 Loc: ColonLoc, /*Entity=*/DeclarationName());
468 TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(T: Ty);
469 ExprResult Cast = BuildCXXNamedCast(
470 OpLoc: ColonLoc, Kind: tok::kw_static_cast, Ty: TSI, E: Element,
471 AngleBrackets: SourceRange(ColonLoc, ColonLoc), Parens: SourceRange(ColonLoc, ColonLoc));
472 assert(!Cast.isInvalid() && "cast to rvalue reference type failed?");
473 Element = Cast.get();
474 }
475
476 Bindings.push_back(Elt: Element);
477 }
478
479 ExprResult Select = BuildCXXExpansionSelectExpr(
480 Range: new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc,
481 /*IsExplicit=*/false),
482 Idx: Index);
483
484 if (Select.isInvalid()) {
485 ActOnInitializerError(Dcl: ExpansionVar);
486 return StmtError();
487 }
488
489 if (FinalizeExpansionVar(S&: *this, ExpansionVar, Initializer: Select))
490 return StmtError();
491
492 return CXXExpansionStmtPattern::CreateDestructuring(
493 Context, ESD, Init, ExpansionVar: ExpansionVarStmt, DecompositionDeclStmt: DS, LParenLoc, ColonLoc, RParenLoc);
494}
495
496StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
497 if (!Exp || !Body)
498 return StmtError();
499
500 auto *Expansion = cast<CXXExpansionStmtPattern>(Val: Exp);
501 assert(!Expansion->getDecl()->getInstantiations() &&
502 "should not rebuild expansion statement after instantiation");
503
504 Expansion->setBody(Body);
505 if (HasDependentSize(CurContext, Pattern: Expansion))
506 return Expansion;
507
508 // Now that we're expanding this, exit the context of the expansion stmt
509 // so that we no longer treat this as dependent.
510 ContextRAII CtxGuard(*this, CurContext->getParent(),
511 /*NewThis=*/false);
512
513 // This can fail if this is an iterating expansion statement.
514 std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion);
515 if (!NumInstantiations)
516 return StmtError();
517
518 // Collect preamble statements.
519 //
520 // There are at most 3 of these: for iterating expansion statements, these
521 // consist of the '__range' and '__begin' variables, and for destructuring
522 // expansion statements of the DecompositionDecl whose initializer we're
523 // expanding. Finally, any expansion statement may have an init-statement
524 // as well.
525 SmallVector<Stmt *, 3> Preamble;
526 if (Expansion->getInit())
527 Preamble.push_back(Elt: Expansion->getInit());
528
529 if (Expansion->isIterating()) {
530 Preamble.push_back(Elt: Expansion->getRangeVarStmt());
531 Preamble.push_back(Elt: Expansion->getBeginVarStmt());
532 } else if (Expansion->isDestructuring()) {
533 Preamble.push_back(Elt: Expansion->getDecompositionDeclStmt());
534 MarkAnyDeclReferenced(Loc: Exp->getBeginLoc(), D: Expansion->getDecompositionDecl(),
535 MightBeOdrUse: true);
536 }
537
538 // Return an empty statement if the range is empty.
539 if (*NumInstantiations == 0) {
540 Expansion->getDecl()->setInstantiations(
541 CXXExpansionStmtInstantiation::Create(C&: Context, Parent: Expansion->getDecl(),
542 /*Instantiations=*/{}, PreambleStmts: Preamble,
543 ShouldApplyLifetimeExtensionToPreamble: Expansion->isDestructuring()));
544 return Expansion;
545 }
546
547 // Create a compound statement binding the expansion variable and body,
548 // as well as the 'iter' variable if this is an iterating expansion statement.
549 SmallVector<Stmt *, 3> StmtsToInstantiate;
550 if (Expansion->isIterating())
551 StmtsToInstantiate.push_back(Elt: Expansion->getIterVarStmt());
552 StmtsToInstantiate.push_back(Elt: Expansion->getExpansionVarStmt());
553 StmtsToInstantiate.push_back(Elt: Body);
554 Stmt *CombinedBody =
555 CompoundStmt::Create(C: Context, Stmts: StmtsToInstantiate, FPFeatures: FPOptionsOverride(),
556 LB: Body->getBeginLoc(), RB: Body->getEndLoc());
557
558 // Expand the body for each instantiation.
559 SmallVector<Stmt *, 4> Instantiations;
560 CXXExpansionStmtDecl *ESD = Expansion->getDecl();
561 QualType PtrDiffT = Context.getPointerDiffType();
562 unsigned PtrDiffTWidth = Context.getIntWidth(T: PtrDiffT);
563 bool PtrDiffTIsUnsigned = PtrDiffT->isUnsignedIntegerType();
564 for (uint64_t I = 0; I < *NumInstantiations; ++I) {
565 llvm::APInt IVal{PtrDiffTWidth, I};
566 TemplateArgument Arg{
567 Context, llvm::APSInt{std::move(IVal), PtrDiffTIsUnsigned}, PtrDiffT};
568 MultiLevelTemplateArgumentList MTArgList(ESD, Arg, true);
569 MTArgList.addOuterRetainedLevels(
570 Num: Expansion->getDecl()->getIndexTemplateParm()->getDepth());
571
572 LocalInstantiationScope LIScope(*this, /*CombineWithOuterScope=*/true);
573 NonSFINAEContext _(*this);
574 InstantiatingTemplate Inst(*this, Body->getBeginLoc(), Expansion, Arg,
575 Body->getSourceRange());
576
577 StmtResult Instantiation = SubstStmt(S: CombinedBody, TemplateArgs: MTArgList);
578 if (Instantiation.isInvalid())
579 return StmtError();
580 Instantiations.push_back(Elt: Instantiation.get());
581 }
582
583 auto *InstantiationsStmt = CXXExpansionStmtInstantiation::Create(
584 C&: Context, Parent: Expansion->getDecl(), Instantiations, PreambleStmts: Preamble,
585 ShouldApplyLifetimeExtensionToPreamble: Expansion->isDestructuring() || Expansion->isIterating());
586
587 Expansion->getDecl()->setInstantiations(InstantiationsStmt);
588 return Expansion;
589}
590
591ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) {
592 if (Idx->isValueDependent() || InitListContainsPack(ILE: Range))
593 return new (Context) CXXExpansionSelectExpr(Context, Range, Idx);
594
595 // The index is a DRE to a template parameter; we should never
596 // fail to evaluate it.
597 uint64_t I = Idx->EvaluateKnownConstInt(Ctx: Context).getZExtValue();
598 return Range->getInit(Init: I);
599}
600
601std::optional<uint64_t>
602Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
603 if (Expansion->isEnumerating())
604 return cast<CXXExpansionSelectExpr>(
605 Val: Expansion->getExpansionVariable()->getInit())
606 ->getRangeExpr()
607 ->getNumInits();
608
609 // [stmt.expand]p5.2 (CWG3131): N is the result of evaluating the expression
610 //
611 // [&] consteval {
612 // std::ptrdiff_t result = 0;
613 // auto b = begin-expr;
614 // auto e = end-expr;
615 // for (; b != e; ++b) ++result;
616 // return result;
617 // }()
618 if (Expansion->isIterating()) {
619 SourceLocation Loc = Expansion->getColonLoc();
620 EnterExpressionEvaluationContext ExprEvalCtx(
621 *this, ExpressionEvaluationContext::ConstantEvaluated);
622
623 // TODO: Build the lambda and evaluate it.
624 Diag(Loc, DiagID: diag::err_iterating_expansion_stmt_unsupported);
625 return std::nullopt;
626
627#if 0 // This will be used once we support iterating expansion statements.
628 Expr::EvalResult ER;
629 SmallVector<PartialDiagnosticAt, 4> Notes;
630 ER.Diag = &Notes;
631 if (!Call.get()->EvaluateAsInt(ER, Context)) {
632 Diag(Loc, diag::err_expansion_size_expr_not_ice);
633 for (const auto &[Location, PDiag] : Notes)
634 Diag(Location, PDiag);
635 return std::nullopt;
636 }
637
638 // It shouldn't be possible for this to be negative since we compute this
639 // via the built-in '++' on a ptrdiff_t.
640 assert(ER.Val.getInt().isNonNegative());
641 return ER.Val.getInt().getZExtValue();
642#endif
643 }
644
645 assert(Expansion->isDestructuring());
646 return Expansion->getDecompositionDecl()->bindings().size();
647}
648