| 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 | |
| 25 | using namespace clang; |
| 26 | |
| 27 | namespace { |
| 28 | struct 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 |
| 57 | static 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 | |
| 63 | static 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 | |
| 74 | static 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 | |
| 79 | static 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 | |
| 105 | static 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 | |
| 239 | static 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 | |
| 279 | CXXExpansionStmtDecl * |
| 280 | Sema::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 | |
| 298 | CXXExpansionStmtDecl * |
| 299 | Sema::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 | |
| 307 | ExprResult Sema::ActOnCXXExpansionInitList(MultiExprArg SubExprs, |
| 308 | SourceLocation LBraceLoc, |
| 309 | SourceLocation RBraceLoc) { |
| 310 | return new (Context) InitListExpr(Context, LBraceLoc, SubExprs, RBraceLoc, |
| 311 | /*IsExplicit=*/true); |
| 312 | } |
| 313 | |
| 314 | StmtResult 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 | |
| 362 | StmtResult 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 | |
| 370 | StmtResult 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 | |
| 496 | StmtResult 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 | |
| 591 | ExprResult 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 | |
| 601 | std::optional<uint64_t> |
| 602 | Sema::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 | |