blob: 779b7add083449d61f6d3998f3c7cb7b56a35716 [file] [edit]
//===-- 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());
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();
}