https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/169682
>From c4c6899f6e48afdf85611f7a66bdf63f456f9bbb Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Tue, 25 Nov 2025 20:47:23 +0100 Subject: [PATCH] [Clang] [C++26] Expansion Statements (Part 3) --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/Sema.h | 22 +++ clang/lib/Frontend/FrontendActions.cpp | 2 + clang/lib/Sema/SemaExpand.cpp | 151 ++++++++++++++++++ clang/lib/Sema/SemaTemplateInstantiate.cpp | 29 +++- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 38 ++++- clang/lib/Sema/SemaTemplateVariadic.cpp | 8 +- clang/lib/Sema/TreeTransform.h | 110 ++++++++++++- .../Parser/cxx2c-expansion-statements.cpp | 78 ++++----- 9 files changed, 390 insertions(+), 50 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ca862316a2f27..d4783c1c9677d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5827,6 +5827,8 @@ def note_template_nsdmi_here : Note< "in instantiation of default member initializer %q0 requested here">; def note_template_type_alias_instantiation_here : Note< "in instantiation of template type alias %0 requested here">; +def note_expansion_stmt_instantiation_here : Note< + "in instantiation of expansion statement requested here">; def note_template_exception_spec_instantiation_here : Note< "in instantiation of exception specification for %0 requested here">; def note_template_requirement_instantiation_here : Note< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 786e53a2e179a..4d25143cffaf4 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13176,6 +13176,9 @@ class Sema final : public SemaBase { /// We are performing partial ordering for template template parameters. PartialOrderingTTP, + + /// We are instantiating an expansion statement. + ExpansionStmtInstantiation, } Kind; /// Whether we're substituting into constraints. @@ -13371,6 +13374,12 @@ class Sema final : public SemaBase { concepts::Requirement *Req, SourceRange InstantiationRange = SourceRange()); + /// \brief Note that we are substituting the body of an expansion statement. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + CXXExpansionStmtPattern *ExpansionStmt, + ArrayRef<TemplateArgument> TArgs, + SourceRange InstantiationRange); + /// \brief Note that we are checking the satisfaction of the constraint /// expression inside of a nested requirement. InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, @@ -15643,6 +15652,19 @@ class Sema final : public SemaBase { ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps); StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body); + + StmtResult BuildCXXEnumeratingExpansionStmtPattern(Decl *ESD, Stmt *Init, + Stmt *ExpansionVar, + SourceLocation LParenLoc, + SourceLocation ColonLoc, + SourceLocation RParenLoc); + + ExprResult + BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range, + Expr *Idx); + + std::optional<uint64_t> + ComputeExpansionSize(CXXExpansionStmtPattern *Expansion); ///@} }; diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index e0c1d304e8290..75d5a76c04a32 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -476,6 +476,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "TypeAliasTemplateInstantiation"; case CodeSynthesisContext::PartialOrderingTTP: return "PartialOrderingTTP"; + case CodeSynthesisContext::ExpansionStmtInstantiation: + return "ExpansionStmtInstantiation"; } return ""; } diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp index c74ed63d3295a..acb28d6bbdcfb 100644 --- a/clang/lib/Sema/SemaExpand.cpp +++ b/clang/lib/Sema/SemaExpand.cpp @@ -24,6 +24,23 @@ using namespace clang; using namespace sema; +// Build a 'DeclRefExpr' designating the template parameter '__N'. +static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) { + return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(), + S.Context.getPointerDiffType(), VK_PRValue, + ESD->getBeginLoc()); +} + +static bool FinaliseExpansionVar(Sema &S, VarDecl *ExpansionVar, + ExprResult Initializer) { + if (Initializer.isInvalid()) { + S.ActOnInitializerError(ExpansionVar); + return true; + } + + S.AddInitializerToDecl(ExpansionVar, Initializer.get(), /*DirectInit=*/false); + return ExpansionVar->isInvalidDecl(); +} CXXExpansionStmtDecl * Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth, @@ -69,13 +86,147 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern( Expr *ExpansionInitializer, SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation RParenLoc, ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) { + if (!ExpansionInitializer || !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() || + ExpansionInitializer->containsErrors()) + return StmtError(); + + // This is an enumerating expansion statement. + if (auto *ILE = dyn_cast<CXXExpansionInitListExpr>(ExpansionInitializer)) { + ExprResult Initializer = + BuildCXXExpansionInitListSelectExpr(ILE, BuildIndexDRE(*this, ESD)); + if (FinaliseExpansionVar(*this, ExpansionVar, Initializer)) + return StmtError(); + + // Note that lifetime extension only applies to destructuring expansion + // statements, so we just ignore 'LifetimeExtendedTemps' entirely for other + // types of expansion statements (this is CWG 3043). + return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc, + ColonLoc, RParenLoc); + } + Diag(ESD->getLocation(), diag::err_expansion_statements_todo); return StmtError(); } +StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern( + Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc) { + return new (Context) CXXEnumeratingExpansionStmtPattern( + cast<CXXExpansionStmtDecl>(ESD), Init, cast<DeclStmt>(ExpansionVar), + 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 (Expansion->hasDependentSize()) + return Expansion; + + // This can fail if this is an iterating expansion statement. + std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion); + if (!NumInstantiations) + return StmtError(); + + // Collect shared statements. + SmallVector<Stmt *, 1> Shared; + if (Expansion->getInit()) + Shared.push_back(Expansion->getInit()); + + assert(isa<CXXEnumeratingExpansionStmtPattern>(Expansion) && "TODO"); + + // Return an empty statement if the range is empty. + if (*NumInstantiations == 0) { + Expansion->getDecl()->setInstantiations( + CXXExpansionStmtInstantiation::Create( + Context, Expansion->getBeginLoc(), Expansion->getEndLoc(), + /*Instantiations=*/{}, Shared, + isa<CXXDestructuringExpansionStmtPattern>(Expansion))); + return Expansion; + } + + // Create a compound statement binding the expansion variable and body. + Stmt *VarAndBody[] = {Expansion->getExpansionVarStmt(), Body}; + Stmt *CombinedBody = + CompoundStmt::Create(Context, VarAndBody, FPOptionsOverride(), + Body->getBeginLoc(), Body->getEndLoc()); + + // Expand the body for each instantiation. + SmallVector<Stmt *, 4> Instantiations; + CXXExpansionStmtDecl *ESD = Expansion->getDecl(); + for (uint64_t I = 0; I < *NumInstantiations; ++I) { + // 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); + + TemplateArgument Arg{Context, llvm::APSInt::get(I), + Context.getPointerDiffType()}; + 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->getBeginLoc(), Expansion->getEndLoc(), Instantiations, + Shared, isa<CXXDestructuringExpansionStmtPattern>(Expansion)); + + Expansion->getDecl()->setInstantiations(InstantiationsStmt); + return Expansion; +} + +ExprResult +Sema::BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range, + Expr *Idx) { + if (Range->containsPackExpansion() || Idx->isValueDependent()) + return new (Context) CXXExpansionInitListSelectExpr(Context, Range, Idx); + + // The index is a DRE to a template parameter; we should never + // fail to evaluate it. + Expr::EvalResult ER; + if (!Idx->EvaluateAsInt(ER, Context)) + llvm_unreachable("Failed to evaluate expansion index"); + + uint64_t I = ER.Val.getInt().getZExtValue(); + return Range->getExprs()[I]; +} + +std::optional<uint64_t> +Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) { + assert(!Expansion->hasDependentSize()); + + if (isa<CXXEnumeratingExpansionStmtPattern>(Expansion)) + return cast<CXXExpansionInitListSelectExpr>( + Expansion->getExpansionVariable()->getInit()) + ->getRangeExpr() + ->getExprs() + .size(); + llvm_unreachable("TODO"); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 35205f40cbcef..5957af710d38e 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -573,6 +573,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case PriorTemplateArgumentSubstitution: case ConstraintsCheck: case NestedRequirementConstraintsCheck: + case ExpansionStmtInstantiation: return true; case RequirementInstantiation: @@ -759,6 +760,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, /*Template=*/nullptr, /*TemplateArgs=*/{}) {} +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, + CXXExpansionStmtPattern *ExpansionStmt, ArrayRef<TemplateArgument> TArgs, + SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::ExpansionStmtInstantiation, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/TArgs) {} + Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, concepts::NestedRequirement *Req, ConstraintsCheck, @@ -1260,6 +1270,9 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) { << /*isTemplateTemplateParam=*/true << Active->InstantiationRange); break; + case CodeSynthesisContext::ExpansionStmtInstantiation: + Diags.Report(Active->PointOfInstantiation, + diag::note_expansion_stmt_instantiation_here); } } } @@ -1894,6 +1907,12 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { maybeInstantiateFunctionParameterToScope(PVD)) return nullptr; + if (isa<CXXExpansionStmtDecl>(D)) { + assert(SemaRef.CurrentInstantiationScope); + return cast<Decl *>( + *SemaRef.CurrentInstantiationScope->findInstantiationOf(D)); + } + return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs); } @@ -2288,9 +2307,13 @@ ExprResult TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E, ValueDecl *PD) { typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; - llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found - = getSema().CurrentInstantiationScope->findInstantiationOf(PD); - assert(Found && "no instantiation for parameter pack"); + llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found = + getSema().CurrentInstantiationScope->getInstantiationOfIfExists(PD); + + // This can happen when instantiating an expansion statement that contains + // a pack (e.g. `template for (auto x : {{ts...}})`). + if (!Found) + return E; Decl *TransformedDecl; if (DeclArgumentPack *Pack = dyn_cast<DeclArgumentPack *>(*Found)) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index f8136c3c24a52..f88ddcb40adf7 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2089,7 +2089,37 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) { Decl *TemplateDeclInstantiator::VisitCXXExpansionStmtDecl( CXXExpansionStmtDecl *OldESD) { - llvm_unreachable("TODO"); + Decl *Index = VisitNonTypeTemplateParmDecl(OldESD->getIndexTemplateParm()); + CXXExpansionStmtDecl *NewESD = SemaRef.BuildCXXExpansionStmtDecl( + Owner, OldESD->getBeginLoc(), cast<NonTypeTemplateParmDecl>(Index)); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldESD, NewESD); + + // If this was already expanded, only instantiate the expansion and + // don't touch the unexpanded expansion statement. + if (CXXExpansionStmtInstantiation *OldInst = OldESD->getInstantiations()) { + StmtResult NewInst = SemaRef.SubstStmt(OldInst, TemplateArgs); + if (NewInst.isInvalid()) + return nullptr; + + NewESD->setInstantiations(NewInst.getAs<CXXExpansionStmtInstantiation>()); + NewESD->setExpansionPattern(OldESD->getExpansionPattern()); + return NewESD; + } + + // Enter the scope of this expansion statement; don't do this if we've + // already expanded it, as in that case we no longer want to treat its + // content as dependent. + Sema::ContextRAII Context(SemaRef, NewESD, /*NewThis=*/false); + + StmtResult Expansion = + SemaRef.SubstStmt(OldESD->getExpansionPattern(), TemplateArgs); + if (Expansion.isInvalid()) + return nullptr; + + // The code that handles CXXExpansionStmtPattern takes care of calling + // setInstantiation() on the ESD if there was an expansion. + NewESD->setExpansionPattern(cast<CXXExpansionStmtPattern>(Expansion.get())); + return NewESD; } Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { @@ -7093,6 +7123,12 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, // anonymous unions in class templates). } + if (CurrentInstantiationScope) { + if (auto Found = CurrentInstantiationScope->getInstantiationOfIfExists(D)) + if (auto *FD = dyn_cast<NamedDecl>(cast<Decl *>(*Found))) + return FD; + } + if (!ParentDependsOnArgs) return D; diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 5b1aad3fa8470..b61148bf95738 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -912,10 +912,14 @@ bool Sema::CheckParameterPacksForExpansion( unsigned NewPackSize, PendingPackExpansionSize = 0; if (IsVarDeclPack) { // Figure out whether we're instantiating to an argument pack or not. + // + // The instantiation may not exist; this can happen when instantiating an + // expansion statement that contains a pack (e.g. + // `template for (auto x : {{ts...}})`). llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation = - CurrentInstantiationScope->findInstantiationOf( + CurrentInstantiationScope->getInstantiationOfIfExists( cast<NamedDecl *>(ParmPack.first)); - if (isa<DeclArgumentPack *>(*Instantiation)) { + if (Instantiation && isa<DeclArgumentPack *>(*Instantiation)) { // We could expand this function parameter pack. NewPackSize = cast<DeclArgumentPack *>(*Instantiation)->size(); } else { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index f181d0abb5dfd..825660fe8174e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -67,6 +67,15 @@ struct UnexpandedInfo { bool ExpandUnderForgetSubstitions = false; }; +/// This contains the common parts that are instantiated for all expansion +/// statement patterns. +struct TransformCXXExpansionStmtPatternResult { + CXXExpansionStmtDecl *NewESD{}; + Stmt *NewInit{}; + DeclStmt *NewExpansionVarDecl{}; + bool isValid() const { return NewESD != nullptr; } +}; + /// A semantic tree transformation that allows one to transform one /// abstract syntax tree into another. /// @@ -857,6 +866,9 @@ class TreeTransform { StmtResult TransformOMPInformationalDirective(OMPExecutableDirective *S); + TransformCXXExpansionStmtPatternResult + TransformCXXExpansionStmtPatternCommonParts(CXXExpansionStmtPattern *S); + // FIXME: We use LLVM_ATTRIBUTE_NOINLINE because inlining causes a ridiculous // amount of stack usage with clang. #define STMT(Node, Parent) \ @@ -9292,10 +9304,49 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) { return FinishCXXForRangeStmt(NewStmt.get(), Body.get()); } +template <typename Derived> +TransformCXXExpansionStmtPatternResult +TreeTransform<Derived>::TransformCXXExpansionStmtPatternCommonParts( + CXXExpansionStmtPattern *S) { + Decl *ESD = + getDerived().TransformDecl(S->getDecl()->getLocation(), S->getDecl()); + if (!ESD || ESD->isInvalidDecl()) + return {}; + + Stmt *Init = S->getInit(); + if (Init) { + StmtResult SR = getDerived().TransformStmt(Init); + if (SR.isInvalid()) + return {}; + Init = SR.get(); + } + + StmtResult ExpansionVar = + getDerived().TransformStmt(S->getExpansionVarStmt()); + if (ExpansionVar.isInvalid()) + return {}; + + return {cast<CXXExpansionStmtDecl>(ESD), Init, + ExpansionVar.getAs<DeclStmt>()}; +} + template <typename Derived> StmtResult TreeTransform<Derived>::TransformCXXEnumeratingExpansionStmtPattern( CXXEnumeratingExpansionStmtPattern *S) { - llvm_unreachable("TOOD"); + TransformCXXExpansionStmtPatternResult Common = + TransformCXXExpansionStmtPatternCommonParts(S); + if (!Common.isValid()) + return StmtError(); + + auto *Expansion = new (SemaRef.Context) CXXEnumeratingExpansionStmtPattern( + Common.NewESD, Common.NewInit, Common.NewExpansionVarDecl, + S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc()); + + StmtResult Body = getDerived().TransformStmt(S->getBody()); + if (Body.isInvalid()) + return StmtError(); + + return SemaRef.FinishCXXExpansionStmt(Expansion, Body.get()); } template <typename Derived> @@ -9324,19 +9375,68 @@ TreeTransform<Derived>::TransformCXXDestructuringExpansionStmtPattern( template <typename Derived> ExprResult TreeTransform<Derived>::TransformCXXExpansionInitListExpr( CXXExpansionInitListExpr *E) { - llvm_unreachable("TOOD"); + bool ArgChanged = false; + SmallVector<Expr *> SubExprs; + if (getDerived().TransformExprs(E->getExprs().data(), E->getExprs().size(), + false, SubExprs, &ArgChanged)) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && !ArgChanged) + return E; + + return CXXExpansionInitListExpr::Create(SemaRef.Context, SubExprs, + E->getLBraceLoc(), E->getRBraceLoc()); } template <typename Derived> StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation( CXXExpansionStmtInstantiation *S) { - llvm_unreachable("TOOD"); + bool SubStmtChanged = false; + auto TransformStmts = [&](SmallVectorImpl<Stmt *> &NewStmts, + ArrayRef<Stmt *> OldStmts) { + for (Stmt *OldDS : OldStmts) { + StmtResult NewDS = getDerived().TransformStmt(OldDS); + if (NewDS.isInvalid()) + return true; + + SubStmtChanged |= NewDS.get() != OldDS; + NewStmts.push_back(NewDS.get()); + } + + return false; + }; + + SmallVector<Stmt *> SharedStmts; + SmallVector<Stmt *> Instantiations; + + if (TransformStmts(SharedStmts, S->getSharedStmts())) + return StmtError(); + + if (TransformStmts(Instantiations, S->getInstantiations())) + return StmtError(); + + if (!getDerived().AlwaysRebuild() && !SubStmtChanged) + return S; + + return CXXExpansionStmtInstantiation::Create( + SemaRef.Context, S->getBeginLoc(), S->getEndLoc(), Instantiations, + SharedStmts, S->shouldApplyLifetimeExtensionToSharedStmts()); } template <typename Derived> ExprResult TreeTransform<Derived>::TransformCXXExpansionInitListSelectExpr( CXXExpansionInitListSelectExpr *E) { - llvm_unreachable("TOOD"); + ExprResult Range = getDerived().TransformExpr(E->getRangeExpr()); + ExprResult Idx = getDerived().TransformExpr(E->getIndexExpr()); + if (Range.isInvalid() || Idx.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && Range.get() == E->getRangeExpr() && + Idx.get() == E->getIndexExpr()) + return E; + + return SemaRef.BuildCXXExpansionInitListSelectExpr( + Range.getAs<CXXExpansionInitListExpr>(), Idx.get()); } template <typename Derived> @@ -15677,7 +15777,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { // will be deemed as dependent even if there are no dependent template // arguments. // (A ClassTemplateSpecializationDecl is always a dependent context.) - while (DC->isRequiresExprBody()) + while (DC->isRequiresExprBody() || isa<CXXExpansionStmtDecl>(DC)) DC = DC->getParent(); if ((getSema().isUnevaluatedContext() || getSema().isConstantEvaluatedContext()) && diff --git a/clang/test/Parser/cxx2c-expansion-statements.cpp b/clang/test/Parser/cxx2c-expansion-statements.cpp index 990bf4dbcc662..308b425b26926 100644 --- a/clang/test/Parser/cxx2c-expansion-statements.cpp +++ b/clang/test/Parser/cxx2c-expansion-statements.cpp @@ -10,54 +10,54 @@ struct initializer_list { void bad() { template for; // expected-error {{expected '(' after 'for'}} - template for (); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}} - template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}} - template for (;;); // expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}} - template for (int x;;); // expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}} - template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}} expected-error {{TODO (expansion statements)}} - template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}} - template for (auto y : {1})]; // expected-error {{expected expression}} expected-error {{TODO (expansion statements)}} - template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{TODO (expansion statements)}} - template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}} expected-error {{TODO (expansion statements)}} - template for (extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}} expected-error {{TODO (expansion statements)}} - template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} expected-error {{TODO (expansion statements)}} - template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}} expected-error {{TODO (expansion statements)}} - template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}} expected-error {{TODO (expansion statements)}} - template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}} expected-error {{TODO (expansion statements)}} - template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} expected-error {{TODO (expansion statements)}} - template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}} expected-error {{TODO (expansion statements)}} - template for (consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}} expected-error {{TODO (expansion statements)}} - template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}} expected-error {{TODO (expansion statements)}} - template for (int x; extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}} expected-error {{TODO (expansion statements)}} - template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} expected-error {{TODO (expansion statements)}} - template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}} expected-error {{TODO (expansion statements)}} - template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}} expected-error {{TODO (expansion statements)}} - template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}} expected-error {{TODO (expansion statements)}} - template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} expected-error {{TODO (expansion statements)}} - template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}} expected-error {{TODO (expansion statements)}} - template for (int x; consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}} expected-error {{TODO (expansion statements)}} - template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}} expected-error {{TODO (expansion statements)}} + template for (); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} + template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} + template for (;;); // expected-error {{expansion statement must be a range-based for loop}} + template for (int x;;); // expected-error {{expansion statement must be a range-based for loop}} + template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}} + template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} + template for (auto y : {1})]; // expected-error {{expected expression}} + template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}} + template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}} + template for (extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}} + template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} + template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}} + template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}} + template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}} + template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} + template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}} + template for (consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}} + template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}} + template for (int x; extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}} + template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} + template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}} + template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}} + template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}} + template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} + template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}} + template for (int x; consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}} + template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}} template for (3 : "error") // expected-error {{expansion statement declaration must declare a variable}} \ - expected-error {{expansion statement must be a range-based for loop}} expected-error {{TODO (expansion statements)}} + expected-error {{expansion statement must be a range-based for loop}} ; template while (true) {} // expected-error {{expected '<' after 'template'}} } void good() { - template for (auto y : {}); // expected-error {{TODO (expansion statements)}} - template for (auto y : {1, 2}); // expected-error {{TODO (expansion statements)}} - template for (int x; auto y : {1, 2}); // expected-error {{TODO (expansion statements)}} - template for (int x; int y : {1, 2}); // expected-error {{TODO (expansion statements)}} - template for (int x; constexpr auto y : {1, 2}); // expected-error {{TODO (expansion statements)}} - template for (int x; constexpr int y : {1, 2}); // expected-error {{TODO (expansion statements)}} - template for (constexpr int a : {1, 2}) { // expected-error {{TODO (expansion statements)}} - template for (constexpr int b : {1, 2}) { // expected-error {{TODO (expansion statements)}} - template for (constexpr int c : {1, 2}); // expected-error {{TODO (expansion statements)}} + template for (auto y : {}); + template for (auto y : {1, 2}); + template for (int x; auto y : {1, 2}); + template for (int x; int y : {1, 2}); + template for (int x; constexpr auto y : {1, 2}); + template for (int x; constexpr int y : {1, 2}); + template for (constexpr int a : {1, 2}) { + template for (constexpr int b : {1, 2}) { + template for (constexpr int c : {1, 2}); } } } void trailing_comma() { - template for (int x : {1, 2,}) {} // expected-error {{TODO (expansion statements)}} - template for (int x : {,}) {} // expected-error {{expected expression}} expected-error {{TODO (expansion statements)}} + template for (int x : {1, 2,}) {} + template for (int x : {,}) {} // expected-error {{expected expression}} } _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
