llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-codegen Author: None (Sirraide) <details> <summary>Changes</summary> --- Patch is 32.88 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169682.diff 9 Files Affected: - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2) - (modified) clang/include/clang/Sema/Sema.h (+22) - (modified) clang/lib/Frontend/FrontendActions.cpp (+2) - (modified) clang/lib/Sema/SemaExpand.cpp (+151) - (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+26-3) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+37-1) - (modified) clang/lib/Sema/SemaTemplateVariadic.cpp (+6-2) - (modified) clang/lib/Sema/TreeTransform.h (+105-5) - (modified) clang/test/Parser/cxx2c-expansion-statements.cpp (+39-39) ``````````diff 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(SemaRe... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/169682 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
