https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/227964
>From f2e20cc513b85149aced18bdf4e0ef6a638dd439 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Thu, 1 Oct 2026 11:28:03 +0530 Subject: [PATCH] [Clang] Fix pack size handling when building alias deduction guides A pack that is rewritten to another pack is a one-element argument pack holding an expansion of unknown length. Treat its size as unknown instead of 1, expand constructor packs declared over a class pack of known size, and refer to the rewritten pack directly when it is substituted without being expanded. Fixes #193217 --- clang/docs/ReleaseNotes.md | 14 ++ clang/include/clang/AST/TemplateBase.h | 4 + clang/include/clang/Sema/Sema.h | 9 ++ clang/lib/AST/TemplateBase.cpp | 26 +++ clang/lib/Sema/SemaTemplateDeduction.cpp | 5 +- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 149 +++++++++++++++-- clang/lib/Sema/SemaTemplateInstantiate.cpp | 111 ++++++++----- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 20 +-- clang/lib/Sema/SemaTemplateVariadic.cpp | 152 +++++++++++------- clang/test/SemaCXX/ctad.cpp | 10 ++ clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 54 +++++++ clang/test/SemaTemplate/pack-deduction.cpp | 8 + 12 files changed, 436 insertions(+), 126 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 7ec126a065ae5ab..f272bac955f927e 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -697,6 +697,20 @@ features cannot lower the translation-unit ABI level; - Fixed concept evaluation bugs where some declarations were not added to the current instantiation scope. (#GH198052) +- Fixed crashes when building the deduction guides of an alias template from a + constructor whose own template parameter packs are declared over, or expanded + together with, a parameter pack of the class template that the alias template + specifies. (#GH193217) + +- Fixed an assertion failure when an empty pack is deduced for a template + parameter pack that was itself expanded from another pack, such as `Ns` in + `template <Ts... Ns>` in a member template of a specialization whose `Ts` is + empty. + +- Fixed a rejected-valid case in CTAD from a constructor template with a + parameter such as `tuple<pair<Ts, Us>...>`, whose nested pack expansion + names packs of both the class template and the constructor template. + - Fixed a crash when a lambda parameter pack was given a default argument that is a pack expansion referencing an enclosing function's parameter pack (e.g. `[](Types... = args...) {}`). Clang now diagnoses the illegal default diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h index f161f6561e742f6..24e8b32f678b431 100644 --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/include/clang/AST/TemplateBase.h @@ -453,6 +453,10 @@ class TemplateArgument { /// the pattern of the pack expansion. TemplateArgument getPackExpansionPattern() const; + /// When the template argument is a pack expansion, returns the number of + /// expansions that it will produce, if known. + UnsignedOrNone getNumExpansions() const; + /// Print this template argument to the given output stream. void print(const PrintingPolicy &Policy, raw_ostream &Out, bool IncludeType) const; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7fbcc4aae7d6c56..fd0167c07929e58 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -14780,6 +14780,15 @@ class Sema final : public SemaBase { void collectUnexpandedParameterPacks( TypeLoc TL, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded); + /// Collect the set of unexpanded parameter packs within the given + /// template parameter list. + /// + /// \param Params The template parameter list that will be traversed to find + /// unexpanded parameter packs. + void collectUnexpandedParameterPacks( + TemplateParameterList *Params, + SmallVectorImpl<UnexpandedParameterPack> &Unexpanded); + /// Collect the set of unexpanded parameter packs within the given /// nested-name-specifier. /// diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index 9356bbadc7a48c3..14dde2b9cb6d4d5 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -536,6 +536,32 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const { llvm_unreachable("Invalid TemplateArgument Kind!"); } +UnsignedOrNone TemplateArgument::getNumExpansions() const { + assert(isPackExpansion()); + + switch (getKind()) { + case Type: + return getAsType()->castAs<PackExpansionType>()->getNumExpansions(); + + case Expression: + return cast<PackExpansionExpr>(getAsExpr())->getNumExpansions(); + + case TemplateExpansion: + return getNumTemplateExpansions(); + + case Declaration: + case Integral: + case StructuralValue: + case Pack: + case Null: + case Template: + case NullPtr: + return std::nullopt; + } + + llvm_unreachable("Invalid TemplateArgument Kind!"); +} + void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out, bool IncludeType) const { diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 2257964a4310bbc..b5724cdbc386d58 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2999,8 +2999,9 @@ ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, } // If the pack is empty, we still need to substitute into the parameter - // itself, in case that substitution fails. - if (SugaredPackedArgsBuilder.empty()) { + // itself, in case that substitution fails. An expanded parameter pack was + // already checked when it was expanded. + if (SugaredPackedArgsBuilder.empty() && !getExpandedPackSize(Param)) { LocalInstantiationScope Scope(S); MultiLevelTemplateArgumentList Args(Template, CTAI.SugaredConverted, /*Final=*/true); diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index fa0918ae30282bc..a3241aeed5298f7 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -337,16 +337,44 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC, return NewTTP; } +// Whether all of the packs in `Unexpanded` have arguments of known size in +// `Args`, as opposed to being rewritten to refer to another parameter pack. +bool packsHaveKnownSize(ArrayRef<UnexpandedParameterPack> Unexpanded, + const MultiLevelTemplateArgumentList &Args) { + if (Unexpanded.empty()) + return false; + for (UnexpandedParameterPack UPP : Unexpanded) { + if (isa_and_present<VarDecl>(dyn_cast<NamedDecl *>(UPP.first))) + return false; + std::optional<std::pair<unsigned, unsigned>> DepthAndIndex = + getDepthAndIndex(UPP); + if (!DepthAndIndex) + return false; + auto [Depth, Index] = *DepthAndIndex; + if (Depth >= Args.getNumLevels() || !Args.hasTemplateArgument(Depth, Index)) + return false; + const TemplateArgument &Arg = Args(Depth, Index); + if (Arg.getKind() != TemplateArgument::Pack || + llvm::any_of(Arg.pack_elements(), [](const TemplateArgument &TA) { + return TA.isPackExpansion() && !TA.getNumExpansions(); + })) + return false; + } + return true; +} + NonTypeTemplateParmDecl * transformTemplateParam(Sema &SemaRef, DeclContext *DC, NonTypeTemplateParmDecl *TTP, unsigned NewDepth, unsigned NewIndex, MultiLevelTemplateArgumentList &Args) { - NonTypeTemplateParmDecl *NewTTP; + TypeSourceInfo *TSI = TTP->getTypeSourceInfo(); + SmallVector<TypeSourceInfo *, 4> ExpandedTypeSourceInfos; + SmallVector<QualType, 4> ExpandedTypes; + bool IsExpandedParameterPack = false; if (TTP->isExpandedParameterPack()) { - SmallVector<TypeSourceInfo *, 4> ExpandedTypeSourceInfos( - TTP->getNumExpansionTypes()); - SmallVector<QualType, 4> ExpandedTypes(TTP->getNumExpansionTypes()); + ExpandedTypeSourceInfos.resize(TTP->getNumExpansionTypes()); + ExpandedTypes.resize(TTP->getNumExpansionTypes()); for (unsigned I = 0, N = TTP->getNumExpansionTypes(); I != N; ++I) { TypeSourceInfo *NewTSI = SemaRef.SubstType(TTP->getExpansionTypeSourceInfo(I), Args, @@ -360,13 +388,55 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC, ExpandedTypeSourceInfos[I] = NewTSI; ExpandedTypes[I] = NewT; } + IsExpandedParameterPack = true; + } else if (TTP->isPackExpansion()) { + // If the packs named by the parameter's type have arguments of known size, + // expand the parameter as template instantiation would. + PackExpansionTypeLoc ExpansionTL = + TSI->getTypeLoc().castAs<PackExpansionTypeLoc>(); + TypeLoc Pattern = ExpansionTL.getPatternLoc(); + SmallVector<UnexpandedParameterPack, 2> Unexpanded; + SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); + if (packsHaveKnownSize(Unexpanded, Args)) { + bool Expand = true; + bool RetainExpansion = false; + UnsignedOrNone NumExpansions = + ExpansionTL.getTypePtr()->getNumExpansions(); + if (SemaRef.CheckParameterPacksForExpansion( + ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(), + Unexpanded, Args, /*FailOnPackProducingTemplates=*/true, Expand, + RetainExpansion, NumExpansions)) + return nullptr; + if (Expand) { + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I); + TypeSourceInfo *NewTSI = SemaRef.SubstType( + Pattern, Args, TTP->getLocation(), TTP->getDeclName()); + if (!NewTSI) + return nullptr; + + QualType NewT = SemaRef.CheckNonTypeTemplateParameterType( + NewTSI, TTP->getLocation()); + if (NewT.isNull()) + return nullptr; + + ExpandedTypeSourceInfos.push_back(NewTSI); + ExpandedTypes.push_back(NewT); + } + IsExpandedParameterPack = true; + } + } + } + + NonTypeTemplateParmDecl *NewTTP; + if (IsExpandedParameterPack) { NewTTP = NonTypeTemplateParmDecl::Create( SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(), NewDepth, - NewIndex, TTP->getIdentifier(), TTP->getType(), - TTP->getTypeSourceInfo(), ExpandedTypes, ExpandedTypeSourceInfos); + NewIndex, TTP->getIdentifier(), TTP->getType(), TSI, ExpandedTypes, + ExpandedTypeSourceInfos); } else { - TypeSourceInfo *NewTSI = SemaRef.SubstType( - TTP->getTypeSourceInfo(), Args, TTP->getLocation(), TTP->getDeclName()); + TypeSourceInfo *NewTSI = + SemaRef.SubstType(TSI, Args, TTP->getLocation(), TTP->getDeclName()); assert(NewTSI); QualType NewT = @@ -378,11 +448,10 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC, NewIndex, TTP->getIdentifier(), NewT, TTP->isParameterPack(), NewTSI); } - if (TypeSourceInfo *TSI = TTP->getTypeSourceInfo(); - AutoTypeLoc AutoLoc = TSI->getTypeLoc().getContainedAutoTypeLoc()) { + if (AutoTypeLoc AutoLoc = TSI->getTypeLoc().getContainedAutoTypeLoc()) { if (AutoLoc.isConstrained()) { SourceLocation EllipsisLoc; - if (TTP->isExpandedParameterPack()) + if (IsExpandedParameterPack) EllipsisLoc = TSI->getTypeLoc().getAs<PackExpansionTypeLoc>().getEllipsisLoc(); else if (auto *Constraint = dyn_cast_if_present<CXXFoldExpr>( @@ -424,15 +493,53 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC, TemplateTemplateParmDecl *TTP, unsigned NewDepth, unsigned NewIndex, MultiLevelTemplateArgumentList &Args, bool EvaluateConstraint) { - TemplateTemplateParmDecl *NewTTP; + SmallVector<TemplateParameterList *, 4> ExpandedTPLs; + bool IsExpandedParameterPack = false; if (TTP->isExpandedParameterPack()) { - SmallVector<TemplateParameterList *, 4> ExpandedTPLs( - TTP->getNumExpansionTemplateParameters()); + ExpandedTPLs.resize(TTP->getNumExpansionTemplateParameters()); for (unsigned I = 0, N = TTP->getNumExpansionTemplateParameters(); I != N; - ++I) + ++I) { ExpandedTPLs[I] = transformTemplateParameters( SemaRef, DC, TTP->getExpansionTemplateParameters(I), Args, NewDepth + 1, EvaluateConstraint); + if (!ExpandedTPLs[I]) + return nullptr; + } + IsExpandedParameterPack = true; + } else if (TTP->isPackExpansion()) { + SmallVector<UnexpandedParameterPack, 2> Unexpanded; + SemaRef.collectUnexpandedParameterPacks(TTP->getTemplateParameters(), + Unexpanded); + if (packsHaveKnownSize(Unexpanded, Args)) { + bool Expand = true; + bool RetainExpansion = false; + UnsignedOrNone NumExpansions = std::nullopt; + if (SemaRef.CheckParameterPacksForExpansion( + TTP->getLocation(), + TTP->getTemplateParameters()->getSourceRange(), Unexpanded, Args, + /*FailOnPackProducingTemplates=*/true, Expand, RetainExpansion, + NumExpansions)) + return nullptr; + if (Expand) { + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I); + // The same template parameters are transformed for each expansion. + LocalInstantiationScope Scope(SemaRef, + /*CombineWithOuterScope=*/true); + TemplateParameterList *Expansion = transformTemplateParameters( + SemaRef, DC, TTP->getTemplateParameters(), Args, NewDepth + 1, + EvaluateConstraint); + if (!Expansion) + return nullptr; + ExpandedTPLs.push_back(Expansion); + } + IsExpandedParameterPack = true; + } + } + } + + TemplateTemplateParmDecl *NewTTP; + if (IsExpandedParameterPack) { NewTTP = TemplateTemplateParmDecl::Create( SemaRef.Context, DC, TTP->getLocation(), NewDepth, NewIndex, TTP->getIdentifier(), TTP->templateParameterKind(), @@ -442,6 +549,8 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC, TemplateParameterList *NewTPL = transformTemplateParameters(SemaRef, DC, TTP->getTemplateParameters(), Args, NewDepth + 1, EvaluateConstraint); + if (!NewTPL) + return nullptr; NewTTP = TemplateTemplateParmDecl::Create( SemaRef.Context, DC, TTP->getLocation(), NewDepth, NewIndex, TTP->isParameterPack(), TTP->getIdentifier(), @@ -489,6 +598,8 @@ transformTemplateParameters(Sema &SemaRef, DeclContext *DC, Params[I] = transformTemplateParameter(SemaRef, DC, TPL->getParam(I), Args, /*NewIndex=*/I, NewDepth, EvaluateConstraint); + if (!Params[I]) + return nullptr; } return TemplateParameterList::Create( SemaRef.Context, TPL->getTemplateLoc(), TPL->getLAngleLoc(), Params, @@ -1028,6 +1139,8 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, NamedDecl *NewParam = transformTemplateParameter( SemaRef, AliasTemplate->getDeclContext(), TP, Args, /*NewIndex=*/N++, getDepthAndIndex(TP).first + AdjustDepth); + if (!NewParam) + return nullptr; TemplateArgument NewTemplateArgument = Context.getInjectedTemplateArg(NewParam); @@ -1054,6 +1167,8 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, NamedDecl *NewParam = transformTemplateParameter( SemaRef, F->getDeclContext(), TP, Args, /*NewIndex=*/N++, getDepthAndIndex(TP).first + AdjustDepth); + if (!NewParam) + return nullptr; assert(TemplateArgsForBuildingRC[Index].isNull()); TemplateArgsForBuildingRC[Index] = Context.getInjectedTemplateArg(NewParam); @@ -1352,6 +1467,8 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( NamedDecl *NewParam = transformTemplateParameter( SemaRef, AliasTemplate->getDeclContext(), TP, Args, /*NewIndex=*/FPrimeTemplateParams.size(), getDepthAndIndex(TP).first); + if (!NewParam) + return nullptr; FPrimeTemplateParams.push_back(NewParam); TemplateArgument NewTemplateArgument = @@ -1428,6 +1545,8 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias( NamedDecl *NewParam = transformTemplateParameter( SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size(), getDepthAndIndex(TP).first); + if (!NewParam) + return nullptr; FPrimeTemplateParams.push_back(NewParam); assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() && diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 036642f22bd8cb1..a07991375e366b3 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2123,6 +2123,20 @@ VarDecl *TemplateInstantiator::RebuildObjCExceptionDecl(VarDecl *ExceptionDecl, return Var; } +/// If the given argument pack is a single pack expansion of unknown length, +/// returns its pattern. A reference to the parameter pack that isn't being +/// expanded is substituted by that pattern, as the enclosing pack expansion +/// expands the packs it names. +static std::optional<TemplateArgument> +getLoneUnexpandedPackExpansionPattern(const TemplateArgument &Pack) { + if (Pack.pack_size() != 1) + return std::nullopt; + const TemplateArgument &Arg = *Pack.pack_begin(); + if (!Arg.isPackExpansion() || Arg.getNumExpansions()) + return std::nullopt; + return Arg.getPackExpansionPattern(); +} + TemplateName TemplateInstantiator::TransformTemplateName( NestedNameSpecifierLoc &QualifierLoc, SourceLocation TemplateKWLoc, TemplateName Name, SourceLocation NameLoc, QualType ObjectType, @@ -2161,15 +2175,20 @@ TemplateName TemplateInstantiator::TransformTemplateName( "Missing argument pack"); if (!getSema().ArgPackSubstIndex) { - // We have the template argument pack to substitute, but we're not - // actually expanding the enclosing pack expansion yet. So, just - // keep the entire argument pack. - return getSema().Context.getSubstTemplateTemplateParmPack( - Arg, AssociatedDecl, TTP->getIndex(), Final); + std::optional<TemplateArgument> Pattern = + getLoneUnexpandedPackExpansionPattern(Arg); + if (!Pattern) { + // We have the template argument pack to substitute, but we're not + // actually expanding the enclosing pack expansion yet. So, just + // keep the entire argument pack. + return getSema().Context.getSubstTemplateTemplateParmPack( + Arg, AssociatedDecl, TTP->getIndex(), Final); + } + Arg = *Pattern; + } else { + PackIndex = SemaRef.getPackIndex(Arg); + Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg); } - - PackIndex = SemaRef.getPackIndex(Arg); - Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg); } TemplateName Template = Arg.getAsTemplate(); @@ -2234,11 +2253,28 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, return Arg.getAsExpr(); } - QualType ParamType = NTTP->isExpandedParameterPack() - ? NTTP->getExpansionType(*SemaRef.ArgPackSubstIndex) - : NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex - ? NTTP->getType().getNonPackExpansionType() - : NTTP->getType(); + bool IsParameterPack = + NTTP->isParameterPack() || + // In concept parameter mapping for fold expressions, packs that aren't + // expanded in place are treated as having non-pack dependency, so that + // a PackExpansionType won't prevent expanding the packs outside the + // TreeTransform. However, we still need to unpack the arguments during + // any template argument substitution, so we also check its FoundDecl. + (E->getFoundDecl() && E->getFoundDecl() != E->getDecl() && + E->getFoundDecl()->isParameterPack()); + + std::optional<TemplateArgument> PackPattern; + if (IsParameterPack && !SemaRef.ArgPackSubstIndex) { + assert(Arg.getKind() == TemplateArgument::Pack && "Missing argument pack"); + PackPattern = getLoneUnexpandedPackExpansionPattern(Arg); + } + + QualType ParamType = + NTTP->isExpandedParameterPack() + ? NTTP->getExpansionType(*SemaRef.ArgPackSubstIndex) + : NTTP->isParameterPack() && (SemaRef.ArgPackSubstIndex || PackPattern) + ? NTTP->getType().getNonPackExpansionType() + : NTTP->getType(); ParamType = SemaRef.SubstType(ParamType, TemplateArgs, E->getLocation(), NTTP->getDeclName()); assert(!ParamType.isNull() && "Shouldn't substitute to an invalid type"); @@ -2246,17 +2282,12 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, auto [AssociatedDecl, Final] = TemplateArgs.getAssociatedDecl(NTTP->getDepth()); UnsignedOrNone PackIndex = std::nullopt; - if (NTTP->isParameterPack() || - // In concept parameter mapping for fold expressions, packs that aren't - // expanded in place are treated as having non-pack dependency, so that - // a PackExpansionType won't prevent expanding the packs outside the - // TreeTransform. However, we still need to unpack the arguments during - // any template argument substitution, so we also check its FoundDecl. - (E->getFoundDecl() && E->getFoundDecl() != E->getDecl() && - E->getFoundDecl()->isParameterPack())) { + if (IsParameterPack) { assert(Arg.getKind() == TemplateArgument::Pack && "Missing argument pack"); - if (!getSema().ArgPackSubstIndex) { + if (PackPattern) { + Arg = *PackPattern; + } else if (!getSema().ArgPackSubstIndex) { // We have an argument pack, but we can't select a particular argument // out of it yet. Therefore, we'll build an expression to hold on to that // argument pack. @@ -2266,9 +2297,10 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, return new (SemaRef.Context) SubstNonTypeTemplateParmPackExpr( ExprType, ParamType->isReferenceType() ? VK_LValue : VK_PRValue, E->getLocation(), Arg, AssociatedDecl, NTTP->getPosition(), Final); + } else { + PackIndex = SemaRef.getPackIndex(Arg); + Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg); } - PackIndex = SemaRef.getPackIndex(Arg); - Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg); } return SemaRef.BuildSubstNonTypeTemplateParmExpr( AssociatedDecl, NTTP->getPosition(), ParamType, E->getLocation(), Arg, @@ -2592,20 +2624,25 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, "Missing argument pack"); if (!getSema().ArgPackSubstIndex) { - // We have the template argument pack, but we're not expanding the - // enclosing pack expansion yet. Just save the template argument - // pack for later substitution. - QualType Result = getSema().Context.getSubstTemplateTypeParmPackType( - AssociatedDecl, T->getIndex(), Final, Arg); - SubstTemplateTypeParmPackTypeLoc NewTL - = TLB.push<SubstTemplateTypeParmPackTypeLoc>(Result); - NewTL.setNameLoc(TL.getNameLoc()); - return Result; + std::optional<TemplateArgument> Pattern = + getLoneUnexpandedPackExpansionPattern(Arg); + if (!Pattern) { + // We have the template argument pack, but we're not expanding the + // enclosing pack expansion yet. Just save the template argument + // pack for later substitution. + QualType Result = getSema().Context.getSubstTemplateTypeParmPackType( + AssociatedDecl, T->getIndex(), Final, Arg); + SubstTemplateTypeParmPackTypeLoc NewTL = + TLB.push<SubstTemplateTypeParmPackTypeLoc>(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; + } + Arg = *Pattern; + } else { + // PackIndex starts from last element. + PackIndex = SemaRef.getPackIndex(Arg); + Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg); } - - // PackIndex starts from last element. - PackIndex = SemaRef.getPackIndex(Arg); - Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg); } assert(Arg.getKind() == TemplateArgument::Type && diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 5e72f05c863a0de..73ffceeb8b70680 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4037,22 +4037,6 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( return Param; } -static void collectUnexpandedParameterPacks( - Sema &S, - TemplateParameterList *Params, - SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) { - for (const auto &P : *Params) { - if (P->isTemplateParameterPack()) - continue; - if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) - S.collectUnexpandedParameterPacks(NTTP->getTypeSourceInfo()->getTypeLoc(), - Unexpanded); - if (TemplateTemplateParmDecl *TTP = dyn_cast<TemplateTemplateParmDecl>(P)) - collectUnexpandedParameterPacks(S, TTP->getTemplateParameters(), - Unexpanded); - } -} - Decl * TemplateDeclInstantiator::VisitTemplateTemplateParmDecl( TemplateTemplateParmDecl *D) { @@ -4085,8 +4069,8 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl( // template parameters. Determine whether we need to expand this parameter // pack into separate parameters. SmallVector<UnexpandedParameterPack, 2> Unexpanded; - collectUnexpandedParameterPacks(SemaRef, D->getTemplateParameters(), - Unexpanded); + SemaRef.collectUnexpandedParameterPacks(D->getTemplateParameters(), + Unexpanded); // Determine whether the set of unexpanded parameter packs can and should // be expanded. diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 2d6e75bd77b402e..83d72a3f19a993d 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -688,6 +688,20 @@ void Sema::collectUnexpandedParameterPacks(QualType T, CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(T); } +void Sema::collectUnexpandedParameterPacks( + TemplateParameterList *Params, + SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) { + for (NamedDecl *P : *Params) { + if (P->isTemplateParameterPack()) + continue; + if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) + collectUnexpandedParameterPacks(NTTP->getTypeSourceInfo()->getTypeLoc(), + Unexpanded); + else if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P)) + collectUnexpandedParameterPacks(TTP->getTemplateParameters(), Unexpanded); + } +} + void Sema::collectUnexpandedParameterPacks( TemplateName Template, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) { @@ -863,6 +877,10 @@ bool Sema::CheckParameterPacksForExpansion( RetainExpansion = false; IdentifierLoc FirstPack; bool HaveFirstPack = false; + UnsignedOrNone OrigNumExpansions = NumExpansions; + // Set if NumExpansions comes from a pack with pack expansions of unknown + // length: the least number of arguments that pack expands to. + UnsignedOrNone LeastTentativeNumExpansions = std::nullopt; UnsignedOrNone NumPartialExpansions = std::nullopt; SourceLocation PartiallySubstitutedPackLoc; typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; @@ -960,19 +978,7 @@ bool Sema::CheckParameterPacksForExpansion( NewPackSize = Pack.size(); PendingPackExpansionSize = llvm::count_if(Pack, [](const TemplateArgument &TA) { - if (!TA.isPackExpansion()) - return false; - - if (TA.getKind() == TemplateArgument::Type) - return !TA.getAsType() - ->castAs<PackExpansionType>() - ->getNumExpansions(); - - if (TA.getKind() == TemplateArgument::Expression) - return !cast<PackExpansionExpr>(TA.getAsExpr()) - ->getNumExpansions(); - - return !TA.getNumTemplateExpansions(); + return TA.isPackExpansion() && !TA.getNumExpansions(); }); } @@ -1001,6 +1007,8 @@ bool Sema::CheckParameterPacksForExpansion( NumExpansions = NewPackSize; FirstPack = IdentifierLoc(ParmPack.second, Name); HaveFirstPack = true; + if (PendingPackExpansionSize) + LeastTentativeNumExpansions = NewPackSize - PendingPackExpansionSize; continue; } @@ -1029,6 +1037,19 @@ bool Sema::CheckParameterPacksForExpansion( unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize; if (PendingPackExpansionSize && LeastNewPackSize <= *NumExpansions) { ShouldExpand = false; + if (LeastTentativeNumExpansions) + LeastTentativeNumExpansions = + std::max(*LeastTentativeNumExpansions, LeastNewPackSize); + continue; + } + // Likewise if the pack NumExpansions comes from may still match this one. + if (LeastTentativeNumExpansions && + *LeastTentativeNumExpansions <= NewPackSize) { + ShouldExpand = false; + if (!PendingPackExpansionSize) { + NumExpansions = NewPackSize; + LeastTentativeNumExpansions = std::nullopt; + } continue; } // C++0x [temp.variadic]p5: @@ -1036,19 +1057,32 @@ bool Sema::CheckParameterPacksForExpansion( // the same number of arguments specified. if (!Diagnose) ; - else if (HaveFirstPack) + else if (!HaveFirstPack) + Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel) + << Name << *NumExpansions << (LeastNewPackSize != NewPackSize) + << LeastNewPackSize << SourceRange(ParmPack.second); + else if (LeastTentativeNumExpansions) + Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict) + << Name << FirstPack.getIdentifierInfo() << NewPackSize + << (*LeastTentativeNumExpansions != *NumExpansions) + << *LeastTentativeNumExpansions << SourceRange(ParmPack.second) + << SourceRange(FirstPack.getLoc()); + else Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict) << FirstPack.getIdentifierInfo() << Name << *NumExpansions << (LeastNewPackSize != NewPackSize) << LeastNewPackSize << SourceRange(FirstPack.getLoc()) << SourceRange(ParmPack.second); - else - Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel) - << Name << *NumExpansions << (LeastNewPackSize != NewPackSize) - << LeastNewPackSize << SourceRange(ParmPack.second); return true; } + + if (!PendingPackExpansionSize) + LeastTentativeNumExpansions = std::nullopt; } + // Only report a tentative NumExpansions when expanding. + if (!ShouldExpand && LeastTentativeNumExpansions) + NumExpansions = OrigNumExpansions; + // If we're performing a partial expansion but we also have a full expansion, // expand to the number of common arguments. For example, given: // @@ -1080,59 +1114,69 @@ UnsignedOrNone Sema::getNumArgumentsInExpansionFromUnexpanded( llvm::ArrayRef<UnexpandedParameterPack> Unexpanded, const MultiLevelTemplateArgumentList &TemplateArgs) { UnsignedOrNone Result = std::nullopt; + bool ResultIsDefinite = false; for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) { - // Compute the depth and index for this parameter pack. - unsigned Depth; - unsigned Index; + unsigned Size; + bool SizeIsDefinite = true; - if (const TemplateTypeParmType *TTP = - dyn_cast<const TemplateTypeParmType *>(Unexpanded[I].first)) { - Depth = TTP->getDepth(); - Index = TTP->getIndex(); - } else if (auto *TST = dyn_cast<const TemplateSpecializationType *>( - Unexpanded[I].first)) { + if (auto *TST = + dyn_cast<const TemplateSpecializationType *>(Unexpanded[I].first)) { // This is a dependent pack, we are not ready to expand it yet. assert(isPackProducingBuiltinTemplateName(TST->getTemplateName())); (void)TST; return std::nullopt; } else if (auto *PST = dyn_cast<const SubstBuiltinTemplatePackType *>( Unexpanded[I].first)) { - assert((!Result || *Result == PST->getNumArgs()) && - "inconsistent pack sizes"); - Result = PST->getNumArgs(); - continue; - } else { - NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first); - if (isa<VarDecl>(ND)) { - // Function parameter pack or init-capture pack. - typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; + Size = PST->getNumArgs(); + } else if (auto *ND = dyn_cast<NamedDecl *>(Unexpanded[I].first); + ND && isa<VarDecl>(ND)) { + // Function parameter pack or init-capture pack. + typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; - llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation = - CurrentInstantiationScope->findInstantiationOf( - cast<NamedDecl *>(Unexpanded[I].first)); - if (isa<Decl *>(*Instantiation)) - // The pattern refers to an unexpanded pack. We're not ready to expand - // this pack yet. - return std::nullopt; + llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation = + CurrentInstantiationScope->findInstantiationOf(ND); + if (isa<Decl *>(*Instantiation)) + // The pattern refers to an unexpanded pack. We're not ready to expand + // this pack yet. + return std::nullopt; - unsigned Size = cast<DeclArgumentPack *>(*Instantiation)->size(); - assert((!Result || *Result == Size) && "inconsistent pack sizes"); - Result = Size; - continue; + Size = cast<DeclArgumentPack *>(*Instantiation)->size(); + } else { + // Compute the depth and index for this parameter pack. + unsigned Depth; + unsigned Index; + if (const TemplateTypeParmType *TTP = + dyn_cast<const TemplateTypeParmType *>(Unexpanded[I].first)) { + Depth = TTP->getDepth(); + Index = TTP->getIndex(); + } else { + std::tie(Depth, Index) = + getDepthAndIndex(cast<NamedDecl *>(Unexpanded[I].first)); } - std::tie(Depth, Index) = getDepthAndIndex(ND); + if (Depth >= TemplateArgs.getNumLevels() || + !TemplateArgs.hasTemplateArgument(Depth, Index)) + // The pattern refers to an unknown template argument. We're not ready + // to expand this pack yet. + return std::nullopt; + + // Determine the size of the argument pack. It isn't definite if the + // pack contains pack expansions of unknown length. + ArrayRef<TemplateArgument> Pack = + TemplateArgs(Depth, Index).getPackAsArray(); + Size = Pack.size(); + SizeIsDefinite = llvm::none_of(Pack, [](const TemplateArgument &TA) { + return TA.isPackExpansion() && !TA.getNumExpansions(); + }); } - if (Depth >= TemplateArgs.getNumLevels() || - !TemplateArgs.hasTemplateArgument(Depth, Index)) - // The pattern refers to an unknown template argument. We're not ready to - // expand this pack yet. + + // A pack of indefinite size may still match the others once substituted. + if (Result && *Result != Size && (!SizeIsDefinite || !ResultIsDefinite)) return std::nullopt; - // Determine the size of the argument pack. - unsigned Size = TemplateArgs(Depth, Index).pack_size(); assert((!Result || *Result == Size) && "inconsistent pack sizes"); Result = Size; + ResultIsDefinite |= SizeIsDefinite; } return Result; diff --git a/clang/test/SemaCXX/ctad.cpp b/clang/test/SemaCXX/ctad.cpp index 6517e88a8a83120..1055d8c4c86e19d 100644 --- a/clang/test/SemaCXX/ctad.cpp +++ b/clang/test/SemaCXX/ctad.cpp @@ -226,3 +226,13 @@ using AliasName = A<T, Ts...>; AliasName aa([](int){}, 0); } + +namespace NestedPackExpansionInConstructor { +template <class...> struct tuple {}; +template <class, class> struct pair {}; +template <class... Ts> struct A { + template <class... Us> A(tuple<pair<Ts, Us>...>); +}; +A a(tuple<pair<int, char>, pair<long, float>>{}); +static_assert(__is_same(decltype(a), A<int, long>)); +} // namespace NestedPackExpansionInConstructor diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 78911cbaed67be1..5b6ce1971c377ac 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -629,3 +629,57 @@ template <typename T> using S3 = S2<T>; // expected-note {{candidate function no // expected-note {{cannot deduce template arguments for 'GH190517::S3' from 'GH190517::S1<char>'}} S3 foo(42); // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'S3'}} } + +namespace GH193217 { + +template <auto...> struct X {}; + +template <typename T, typename... Ts> struct A { + template <Ts... Ns, T *...Ps> A(X<Ps...>, Ts (*...qs)[Ns]); +}; + +template <class T = int, class U = T> using AA = A<U>; // #GH193217_AA + +AA a{}; +// expected-error@-1 {{no viable constructor or deduction guide for deduction of template arguments of 'AA'}} +// expected-note@#GH193217_AA 2{{candidate function template not viable}} +// expected-note@#GH193217_AA 2{{implicit deduction guide declared as}} + +int *p; +AA a1{X<&p>{}}; +static_assert(__is_same(decltype(a1), A<int *>)); + +template <class T = int, class U = T> using AB = A<U, int>; +int arr[3]; +AB b{X<&p>{}, &arr}; +static_assert(__is_same(decltype(b), A<int *, int>)); + +template <class T = int, class U = T> using AC = A<U, int, long>; +long arr2[4]; +AC c{X<&p>{}, &arr, &arr2}; +static_assert(__is_same(decltype(c), A<int *, int, long>)); + +template <auto, class> struct Y {}; +template <class T, class... Ts> struct B { + template <Ts... Ns> B(T, Y<Ns, Ts>...); +}; +template <class U> using BB = B<U>; +BB bb{0}; +static_assert(__is_same(decltype(bb), B<int>)); + +template <class, class> struct P {}; +template <class T, class... Ts> struct C { + template <class... Us> C(T, P<Ts, Us>...); +}; +template <class U> using CC = C<U>; +CC cc{0}; +static_assert(__is_same(decltype(cc), C<int>)); + +template <class T, class... Ts> struct D { + template <template <Ts> class... TTs> D(T); +}; +template <class U> using DD = D<U>; +DD dd{0}; +static_assert(__is_same(decltype(dd), D<int>)); + +} // namespace GH193217 diff --git a/clang/test/SemaTemplate/pack-deduction.cpp b/clang/test/SemaTemplate/pack-deduction.cpp index b3104609994a4e1..8b8db2889ce84fb 100644 --- a/clang/test/SemaTemplate/pack-deduction.cpp +++ b/clang/test/SemaTemplate/pack-deduction.cpp @@ -258,3 +258,11 @@ template <int... Args> using Alias = Nttp<1, 2, 3>::B<Args...>; } } + +namespace ExpandedPackDeducedEmpty { +template <int...> struct X {}; +template <typename... Ts> struct A { + template <int N, Ts... Ns> A(X<N>, X<Ns...>); +}; +A<> a{X<1>{}, X<>{}}; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
