| //===-- SemaExpand.cpp - Semantic Analysis for Expansion Statements--------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements semantic analysis for C++26 expansion statements, | |
| // aka 'template for'. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "clang/AST/DeclCXX.h" | |
| #include "clang/AST/ExprCXX.h" | |
| #include "clang/AST/StmtCXX.h" | |
| #include "clang/Lex/Preprocessor.h" | |
| #include "clang/Sema/EnterExpressionEvaluationContext.h" | |
| #include "clang/Sema/Lookup.h" | |
| #include "clang/Sema/Overload.h" | |
| #include "clang/Sema/Sema.h" | |
| #include "clang/Sema/Template.h" | |
| #include "llvm/ADT/ScopeExit.h" | |
| using namespace clang; | |
| namespace { | |
| struct IterableExpansionStmtData { | |
| /// When we try to determine whether an expansion statement is iterating, the | |
| /// result can be | |
| /// | |
| /// 1. it is not iterating (it could still be destructuring; we don't | |
| /// know that yet); | |
| /// | |
| /// 2. there was a hard error (e.g. we determined that it is iterating, | |
| /// but begin() is deleted); | |
| /// | |
| /// 3. it is iterating, and there was no error. | |
| enum class IsIterableResult { | |
| NotIterable, ///< Not iterable, but also not invalid. | |
| Error, ///< Ill-formed. | |
| Iterable, ///< Iterable (and there was no error). | |
| }; | |
| DeclStmt *RangeDecl = nullptr; | |
| DeclStmt *BeginDecl = nullptr; | |
| DeclStmt *IterDecl = nullptr; | |
| IsIterableResult TheState = IsIterableResult::NotIterable; | |
| bool isIterable() const { return TheState == IsIterableResult::Iterable; } | |
| bool hasError() { return TheState == IsIterableResult::Error; } | |
| }; | |
| } // namespace | |
| // Build a 'DeclRefExpr' designating the template parameter that is used as | |
| // the expansion index | |
| static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) { | |
| return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(), | |
| S.Context.getPointerDiffType(), VK_PRValue, | |
| ESD->getBeginLoc()); | |
| } | |
| static bool FinalizeExpansionVar(Sema &S, VarDecl *ExpansionVar, | |
| ExprResult Initializer) { | |
| if (Initializer.isInvalid()) { | |
| S.ActOnInitializerError(ExpansionVar); | |
| return true; | |
| } | |
| S.AddInitializerToDecl(ExpansionVar, Initializer.get(), /*DirectInit=*/false); | |
| return ExpansionVar->isInvalidDecl(); | |
| } | |
| static auto InitListContainsPack(const InitListExpr *ILE) { | |
| return llvm::any_of(ILE->inits(), | |
| [](const Expr *E) { return isa<PackExpansionExpr>(E); }); | |
| } | |
| static bool HasDependentSize(const DeclContext *CurContext, | |
| const CXXExpansionStmtPattern *Pattern) { | |
| switch (Pattern->getKind()) { | |
| case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: { | |
| auto *SelectExpr = cast<CXXExpansionSelectExpr>( | |
| Pattern->getExpansionVariable()->getInit()); | |
| return InitListContainsPack(SelectExpr->getRangeExpr()); | |
| } | |
| case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: | |
| // Even if the size isn't technically dependent, delay expansion until | |
| // we're no longer in a template since evaluating a lambda declared in | |
| // a template doesn't work too well. | |
| assert(CurContext->isExpansionStmt()); | |
| return CurContext->getParent()->isDependentContext(); | |
| case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent: | |
| return true; | |
| case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: | |
| return false; | |
| } | |
| llvm_unreachable("invalid pattern kind"); | |
| } | |
| static IterableExpansionStmtData TryBuildIterableExpansionStmtInitializer( | |
| Sema &S, Expr *ExpansionInitializer, Expr *Index, SourceLocation ColonLoc, | |
| bool VarIsConstexpr, | |
| ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) { | |
| 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); | |
| 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); | |
| } | |
| // If member lookup doesn't yield anything, 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->getParent(), | |
| /*NewThis=*/false); | |
| // 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::IsIterableResult::Error; | |
| Scope *Scope = S.getCurScope(); | |
| // By [stmt.expand]p5.2 (CWG3131), 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(DeducedKind::Undeduced, QualType(), | |
| AutoTypeKeyword::DecltypeAuto), | |
| VarIsConstexpr); | |
| if (Var.isInvalid()) | |
| return Data; | |
| // [stmt.expand]p5.2 (CWG3140): The keyword 'constexpr' is present in the | |
| // declarations of 'range', 'begin', and 'iter' if and only if 'constexpr' is | |
| // one of the decl-specifiers of the decl-specifier-seq of the | |
| // for-range-declaration. | |
| // | |
| // FIXME: As of CWG3140, we should only create 'begin' here, and not 'end', | |
| // but that requires another substantial refactor of the for-range code. | |
| auto *RangeVar = cast<DeclStmt>(Var.get()); | |
| Sema::ForRangeBeginEndInfo Info = S.BuildCXXForRangeBeginEndVars( | |
| Scope, cast<VarDecl>(RangeVar->getSingleDecl()), ColonLoc, | |
| /*CoawaitLoc=*/{}, | |
| /*LifetimeExtendTemps=*/{}, Sema::BFRK_Build, VarIsConstexpr); | |
| if (!Info.isValid()) | |
| return Data; | |
| // At runtime, we only need to evaluate 'begin', whereas 'end' is only used at | |
| // compile-time; we'll rebuild the latter when we compute the expansion size, | |
| // so only build a DeclStmt for 'begin' here. | |
| StmtResult BeginStmt = S.ActOnDeclStmt( | |
| S.ConvertDeclToDeclGroup(Info.BeginVar), ColonLoc, ColonLoc); | |
| if (BeginStmt.isInvalid()) | |
| return Data; | |
| // TODO: Build 'constexpr auto iter = begin + decltype(begin - begin){i};'. | |
| S.Diag(ColonLoc, diag::err_iterating_expansion_stmt_unsupported); | |
| return Data; | |
| #if 0 // This will be used once we support iterating expansion statements. | |
| // Store it in a variable. | |
| // See also Sema::BuildCXXForRangeBeginEndVars(). | |
| const auto DepthStr = std::to_string(Scope->getDepth() / 2); | |
| IdentifierInfo *Name = | |
| S.PP.getIdentifierInfo(std::string("__iter") + DepthStr); | |
| VarDecl *IterVar = S.BuildForRangeVarDecl( | |
| ColonLoc, S.Context.getAutoDeductType(), Name, VarIsConstexpr); | |
| S.AddInitializerToDecl(IterVar, BeginPlusI.get(), /*DirectInit=*/false); | |
| if (IterVar->isInvalidDecl()) | |
| return Data; | |
| StmtResult IterVarStmt = | |
| S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(IterVar), ColonLoc, ColonLoc); | |
| if (IterVarStmt.isInvalid()) | |
| return Data; | |
| // CWG 3149: Apply lifetime extension to iterating expansion statements. | |
| S.ApplyForRangeOrExpansionStatementLifetimeExtension( | |
| cast<VarDecl>(RangeVar->getSingleDecl()), LifetimeExtendTemps); | |
| Data.BeginDecl = BeginStmt.getAs<DeclStmt>(); | |
| Data.RangeDecl = RangeVar; | |
| Data.IterDecl = IterVarStmt.getAs<DeclStmt>(); | |
| Data.TheState = IterableExpansionStmtData::IsIterableResult::Iterable; | |
| return Data; | |
| #endif | |
| } | |
| static StmtResult BuildDestructuringDecompositionDecl( | |
| Sema &S, Expr *ExpansionInitializer, SourceLocation ColonLoc, | |
| bool VarIsConstexpr, | |
| ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) { | |
| 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->getParent(), | |
| /*NewThis=*/false); | |
| UnsignedOrNone Arity = | |
| S.GetDecompositionElementCount(ExpansionInitializer->getType(), ColonLoc); | |
| if (!Arity) | |
| return StmtError(); | |
| QualType AutoRRef = S.Context.getAutoRRefDeductType(); | |
| SmallVector<BindingDecl *> Bindings; | |
| for (unsigned I = 0; I < *Arity; ++I) | |
| Bindings.push_back(BindingDecl::Create( | |
| S.Context, S.CurContext, ColonLoc, | |
| S.getPreprocessor().getIdentifierInfo("__u" + std::to_string(I)), | |
| AutoRRef)); | |
| TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(AutoRRef); | |
| auto *DD = | |
| DecompositionDecl::Create(S.Context, S.CurContext, ColonLoc, ColonLoc, | |
| ColonLoc, AutoRRef, TSI, SC_Auto, Bindings); | |
| if (VarIsConstexpr) | |
| DD->setConstexpr(true); | |
| S.ApplyForRangeOrExpansionStatementLifetimeExtension(DD, LifetimeExtendTemps); | |
| S.AddInitializerToDecl(DD, ExpansionInitializer, false); | |
| return S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(DD), ColonLoc, ColonLoc); | |
| } | |
| CXXExpansionStmtDecl * | |
| Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth, | |
| SourceLocation TemplateKWLoc) { | |
| // Create a template parameter. This will be used to denote the index | |
| // of the element that we're instantiating. This type must be 'ptrdiff_t' | |
| // for iterating expansion statements ([stmt.expand]p5.2), so use that in | |
| // all cases. | |
| QualType ParmTy = Context.getPointerDiffType(); | |
| TypeSourceInfo *ParmTI = | |
| Context.getTrivialTypeSourceInfo(ParmTy, TemplateKWLoc); | |
| auto *TParam = NonTypeTemplateParmDecl::Create( | |
| Context, Context.getTranslationUnitDecl(), TemplateKWLoc, TemplateKWLoc, | |
| TemplateDepth, /*Position=*/0, /*Id=*/nullptr, ParmTy, | |
| /*ParameterPack=*/false, ParmTI); | |
| return BuildCXXExpansionStmtDecl(CurContext, TemplateKWLoc, TParam); | |
| } | |
| CXXExpansionStmtDecl * | |
| Sema::BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc, | |
| NonTypeTemplateParmDecl *NTTP) { | |
| auto *Result = | |
| CXXExpansionStmtDecl::Create(Context, Ctx, TemplateKWLoc, NTTP); | |
| Ctx->addDecl(Result); | |
| return Result; | |
| } | |
| ExprResult Sema::ActOnCXXExpansionInitList(MultiExprArg SubExprs, | |
| SourceLocation LBraceLoc, | |
| SourceLocation RBraceLoc) { | |
| return new (Context) InitListExpr(Context, LBraceLoc, SubExprs, RBraceLoc, | |
| /*IsExplicit=*/true); | |
| } | |
| StmtResult Sema::ActOnCXXExpansionStmtPattern( | |
| CXXExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt, | |
| Expr *ExpansionInitializer, SourceLocation LParenLoc, | |
| SourceLocation ColonLoc, SourceLocation RParenLoc, | |
| ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) { | |
| if (!ExpansionInitializer || ExpansionInitializer->containsErrors() || | |
| !ExpansionVarStmt) | |
| return StmtError(); | |
| assert(CurContext->isExpansionStmt()); | |
| auto *DS = cast<DeclStmt>(ExpansionVarStmt); | |
| if (!DS->isSingleDecl()) { | |
| Diag(DS->getBeginLoc(), diag::err_type_defined_in_for_range); | |
| return StmtError(); | |
| } | |
| VarDecl *ExpansionVar = dyn_cast<VarDecl>(DS->getSingleDecl()); | |
| if (!ExpansionVar || ExpansionVar->isInvalidDecl()) | |
| return StmtError(); | |
| // This is an enumerating expansion statement. | |
| if (auto *ILE = dyn_cast<InitListExpr>(ExpansionInitializer)) { | |
| assert(ILE->isSyntacticForm()); | |
| ExprResult Initializer = | |
| BuildCXXExpansionSelectExpr(ILE, BuildIndexDRE(*this, ESD)); | |
| if (FinalizeExpansionVar(*this, ExpansionVar, Initializer)) | |
| return StmtError(); | |
| // TODO: CWG3043 (lifetime extension in enumerating expansion statements). | |
| return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc, | |
| ColonLoc, RParenLoc); | |
| } | |
| if (ExpansionInitializer->hasPlaceholderType()) { | |
| ExprResult R = CheckPlaceholderExpr(ExpansionInitializer); | |
| if (R.isInvalid()) | |
| return StmtError(); | |
| ExpansionInitializer = R.get(); | |
| } | |
| if (DiagnoseUnexpandedParameterPack(ExpansionInitializer)) | |
| return StmtError(); | |
| return BuildNonEnumeratingCXXExpansionStmtPattern( | |
| ESD, Init, DS, ExpansionInitializer, LParenLoc, ColonLoc, RParenLoc, | |
| LifetimeExtendTemps); | |
| } | |
| StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern( | |
| Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc, | |
| SourceLocation ColonLoc, SourceLocation RParenLoc) { | |
| return CXXExpansionStmtPattern::CreateEnumerating( | |
| Context, cast<CXXExpansionStmtDecl>(ESD), Init, | |
| cast<DeclStmt>(ExpansionVar), LParenLoc, ColonLoc, RParenLoc); | |
| } | |
| StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern( | |
| CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt, | |
| Expr *ExpansionInitializer, SourceLocation LParenLoc, | |
| SourceLocation ColonLoc, SourceLocation RParenLoc, | |
| ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) { | |
| VarDecl *ExpansionVar = cast<VarDecl>(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 CXXExpansionStmtPattern::CreateDependent( | |
| Context, ESD, Init, ExpansionVarStmt, ExpansionInitializer, LParenLoc, | |
| ColonLoc, 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( | |
| *this, ExpansionInitializer, Index, ColonLoc, ExpansionVar->isConstexpr(), | |
| LifetimeExtendTemps); | |
| if (Data.hasError()) { | |
| ActOnInitializerError(ExpansionVar); | |
| return StmtError(); | |
| } | |
| if (Data.isIterable()) { | |
| // Build '*iter'. | |
| auto *IterVar = cast<VarDecl>(Data.IterDecl->getSingleDecl()); | |
| DeclRefExpr *IterDRE = BuildDeclRefExpr( | |
| IterVar, IterVar->getType().getNonReferenceType(), VK_LValue, ColonLoc); | |
| ExprResult Deref = | |
| ActOnUnaryOp(getCurScope(), ColonLoc, tok::star, IterDRE); | |
| if (Deref.isInvalid()) { | |
| ActOnInitializerError(ExpansionVar); | |
| return StmtError(); | |
| } | |
| Deref = MaybeCreateExprWithCleanups(Deref.get()); | |
| if (FinalizeExpansionVar(*this, ExpansionVar, Deref.get())) | |
| return StmtError(); | |
| return CXXExpansionStmtPattern::CreateIterating( | |
| Context, ESD, Init, ExpansionVarStmt, Data.RangeDecl, Data.BeginDecl, | |
| Data.IterDecl, LParenLoc, ColonLoc, RParenLoc); | |
| } | |
| // If not, try destructuring. | |
| StmtResult DecompDeclStmt = BuildDestructuringDecompositionDecl( | |
| *this, ExpansionInitializer, ColonLoc, ExpansionVar->isConstexpr(), | |
| LifetimeExtendTemps); | |
| if (DecompDeclStmt.isInvalid()) { | |
| Diag(ExpansionInitializer->getBeginLoc(), | |
| diag::err_expansion_stmt_invalid_init) | |
| << ExpansionInitializer->getType() | |
| << ExpansionInitializer->getSourceRange(); | |
| ActOnInitializerError(ExpansionVar); | |
| return StmtError(); | |
| } | |
| auto *DS = DecompDeclStmt.getAs<DeclStmt>(); | |
| auto *DD = cast<DecompositionDecl>(DS->getSingleDecl()); | |
| if (DD->isInvalidDecl()) | |
| return StmtError(); | |
| // Synthesise an InitListExpr to store the bindings; this essentially lets us | |
| // desugar the expansion of a destructuring expansion statement to that of an | |
| // enumerating expansion statement. | |
| SmallVector<Expr *> Bindings; | |
| Bindings.reserve(DD->bindings().size()); | |
| for (BindingDecl *BD : DD->bindings()) { | |
| Expr *Element = BuildDeclRefExpr(BD, BD->getType().getNonReferenceType(), | |
| VK_LValue, ColonLoc); | |
| // [stmt.expand]p5.3 (CWG3149): If the expansion-initializer is an lvalue, | |
| // then vi is ui; otherwise, vi is static_cast<decltype(ui)&&>(ui). | |
| if (!ExpansionInitializer->isLValue()) { | |
| QualType Ty = | |
| BuildReferenceType(getDecltypeForExpr(Element), /*LValueRef=*/false, | |
| ColonLoc, /*Entity=*/DeclarationName()); | |
| TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(Ty); | |
| ExprResult Cast = BuildCXXNamedCast( | |
| ColonLoc, tok::kw_static_cast, TSI, Element, | |
| SourceRange(ColonLoc, ColonLoc), SourceRange(ColonLoc, ColonLoc)); | |
| assert(!Cast.isInvalid() && "cast to rvalue reference type failed?"); | |
| Element = Cast.get(); | |
| } | |
| Bindings.push_back(Element); | |
| } | |
| ExprResult Select = BuildCXXExpansionSelectExpr( | |
| new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc, | |
| /*IsExplicit=*/false), | |
| Index); | |
| if (Select.isInvalid()) { | |
| ActOnInitializerError(ExpansionVar); | |
| return StmtError(); | |
| } | |
| if (FinalizeExpansionVar(*this, ExpansionVar, Select)) | |
| return StmtError(); | |
| return CXXExpansionStmtPattern::CreateDestructuring( | |
| Context, ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc); | |
| } | |
| StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { | |
| if (!Exp || !Body) | |
| return StmtError(); | |
| auto *Expansion = cast<CXXExpansionStmtPattern>(Exp); | |
| assert(!Expansion->getDecl()->getInstantiations() && | |
| "should not rebuild expansion statement after instantiation"); | |
| Expansion->setBody(Body); | |
| if (HasDependentSize(CurContext, 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); | |
| // This can fail if this is an iterating expansion statement. | |
| std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion); | |
| if (!NumInstantiations) | |
| return StmtError(); | |
| // Collect preamble statements. | |
| // | |
| // There are at most 3 of these: for iterating expansion statements, these | |
| // consist of the '__range' and '__begin' variables, and for destructuring | |
| // expansion statements of the DecompositionDecl whose initializer we're | |
| // expanding. Finally, any expansion statement may have an init-statement | |
| // as well. | |
| SmallVector<Stmt *, 3> Preamble; | |
| if (Expansion->getInit()) | |
| Preamble.push_back(Expansion->getInit()); | |
| if (Expansion->isIterating()) { | |
| Preamble.push_back(Expansion->getRangeVarStmt()); | |
| Preamble.push_back(Expansion->getBeginVarStmt()); | |
| } else if (Expansion->isDestructuring()) { | |
| Preamble.push_back(Expansion->getDecompositionDeclStmt()); | |
| MarkAnyDeclReferenced(Exp->getBeginLoc(), Expansion->getDecompositionDecl(), | |
| true); | |
| } | |
| // Return an empty statement if the range is empty. | |
| if (*NumInstantiations == 0) { | |
| Expansion->getDecl()->setInstantiations( | |
| CXXExpansionStmtInstantiation::Create(Context, Expansion->getDecl(), | |
| /*Instantiations=*/{}, Preamble, | |
| Expansion->isDestructuring())); | |
| return Expansion; | |
| } | |
| // Create a compound statement binding the expansion variable and body, | |
| // as well as the 'iter' variable if this is an iterating expansion statement. | |
| SmallVector<Stmt *, 3> StmtsToInstantiate; | |
| if (Expansion->isIterating()) | |
| StmtsToInstantiate.push_back(Expansion->getIterVarStmt()); | |
| StmtsToInstantiate.push_back(Expansion->getExpansionVarStmt()); | |
| StmtsToInstantiate.push_back(Body); | |
| Stmt *CombinedBody = | |
| CompoundStmt::Create(Context, StmtsToInstantiate, FPOptionsOverride(), | |
| Body->getBeginLoc(), Body->getEndLoc()); | |
| // Expand the body for each instantiation. | |
| SmallVector<Stmt *, 4> Instantiations; | |
| CXXExpansionStmtDecl *ESD = Expansion->getDecl(); | |
| QualType PtrDiffT = Context.getPointerDiffType(); | |
| unsigned PtrDiffTWidth = Context.getIntWidth(PtrDiffT); | |
| bool PtrDiffTIsUnsigned = PtrDiffT->isUnsignedIntegerType(); | |
| for (uint64_t I = 0; I < *NumInstantiations; ++I) { | |
| llvm::APInt IVal{PtrDiffTWidth, I}; | |
| TemplateArgument Arg{ | |
| Context, llvm::APSInt{std::move(IVal), PtrDiffTIsUnsigned}, PtrDiffT}; | |
| MultiLevelTemplateArgumentList MTArgList(ESD, Arg, true); | |
| MTArgList.addOuterRetainedLevels( | |
| Expansion->getDecl()->getIndexTemplateParm()->getDepth()); | |
| LocalInstantiationScope LIScope(*this, /*CombineWithOuterScope=*/true); | |
| NonSFINAEContext _(*this); | |
| InstantiatingTemplate Inst(*this, Body->getBeginLoc(), Expansion, Arg, | |
| Body->getSourceRange()); | |
| if (Inst.isInvalid()) | |
| return StmtError(); | |
| StmtResult Instantiation = SubstStmt(CombinedBody, MTArgList); | |
| if (Instantiation.isInvalid()) | |
| return StmtError(); | |
| Instantiations.push_back(Instantiation.get()); | |
| } | |
| auto *InstantiationsStmt = CXXExpansionStmtInstantiation::Create( | |
| Context, Expansion->getDecl(), Instantiations, Preamble, | |
| Expansion->isDestructuring() || Expansion->isIterating()); | |
| Expansion->getDecl()->setInstantiations(InstantiationsStmt); | |
| return Expansion; | |
| } | |
| ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) { | |
| if (Idx->isValueDependent() || InitListContainsPack(Range)) | |
| return new (Context) CXXExpansionSelectExpr(Context, Range, Idx); | |
| // The index is a DRE to a template parameter; we should never | |
| // fail to evaluate it. | |
| uint64_t I = Idx->EvaluateKnownConstInt(Context).getZExtValue(); | |
| return Range->getInit(I); | |
| } | |
| std::optional<uint64_t> | |
| Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { | |
| if (Expansion->isEnumerating()) | |
| return cast<CXXExpansionSelectExpr>( | |
| Expansion->getExpansionVariable()->getInit()) | |
| ->getRangeExpr() | |
| ->getNumInits(); | |
| // [stmt.expand]p5.2 (CWG3131): N is the result of evaluating the expression | |
| // | |
| // [&] consteval { | |
| // std::ptrdiff_t result = 0; | |
| // auto b = begin-expr; | |
| // auto e = end-expr; | |
| // for (; b != e; ++b) ++result; | |
| // return result; | |
| // }() | |
| if (Expansion->isIterating()) { | |
| SourceLocation Loc = Expansion->getColonLoc(); | |
| EnterExpressionEvaluationContext ExprEvalCtx( | |
| *this, ExpressionEvaluationContext::ConstantEvaluated); | |
| // TODO: Build the lambda and evaluate it. | |
| Diag(Loc, diag::err_iterating_expansion_stmt_unsupported); | |
| return std::nullopt; | |
| #if 0 // This will be used once we support iterating expansion statements. | |
| Expr::EvalResult ER; | |
| SmallVector<PartialDiagnosticAt, 4> Notes; | |
| ER.Diag = &Notes; | |
| 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; | |
| } | |
| // 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(); | |
| #endif | |
| } | |
| assert(Expansion->isDestructuring()); | |
| return Expansion->getDecompositionDecl()->bindings().size(); | |
| } |