diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5d4b182f29afa..7801bad343914 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -325,6 +325,10 @@ C++20 Feature Support - Implemented module level lookup for C++20 modules. (#GH90154) +C++17 Feature Support +^^^^^^^^^^^^^^^^^^^^^ +- The implementation of the relaxed template template argument matching rules is + more complete and reliable, and should provide more accurate diagnostics. Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -351,7 +355,8 @@ Resolutions to C++ Defect Reports (`CWG2351: void{} `_). - Clang now has improved resolution to CWG2398, allowing class templates to have - default arguments deduced when partial ordering. + default arguments deduced when partial ordering, and better backwards compatibility + in overload resolution. - Clang now allows comparing unequal object pointers that have been cast to ``void *`` in constant expressions. These comparisons always worked in non-constant expressions. @@ -636,6 +641,10 @@ Improvements to Clang's diagnostics - Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391. +- Clang now properly explains the reason a template template argument failed to + match a template template parameter, in terms of the C++17 relaxed matching rules + instead of the old ones. + - Don't emit duplicated dangling diagnostics. (#GH93386). - Improved diagnostic when trying to befriend a concept. (#GH45182). @@ -885,6 +894,8 @@ Bug Fixes to C++ Support - Correctly check constraints of explicit instantiations of member functions. (#GH46029) - When performing partial ordering of function templates, clang now checks that the deduction was consistent. Fixes (#GH18291). +- Fixes to several issues in partial ordering of template template parameters, which + were documented in the test suite. - Fixed an assertion failure about a constraint of a friend function template references to a value with greater template depth than the friend function template. (#GH98258) - Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 36b693c6a304e..774e5484cfa0e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5323,6 +5323,13 @@ def note_template_arg_refers_here_func : Note< def err_template_arg_template_params_mismatch : Error< "template template argument has different template parameters than its " "corresponding template template parameter">; +def note_template_arg_template_params_mismatch : Note< + "template template argument has different template parameters than its " + "corresponding template template parameter">; +def err_non_deduced_mismatch : Error< + "could not match %diff{$ against $|types}0,1">; +def err_inconsistent_deduction : Error< + "conflicting deduction %diff{$ against $|types}0,1 for parameter">; def err_template_arg_not_integral_or_enumeral : Error< "non-type template argument of type %0 must have an integral or enumeration" " type">; diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index 176a2a8d2a35e..c7f2422b542dd 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -930,6 +930,11 @@ class Sema; LLVM_PREFERRED_TYPE(bool) unsigned TookAddressOfOverload : 1; + /// Have we matched any packs on the parameter side, versus any non-packs on + /// the argument side, in a context where the opposite matching is also + /// allowed? + bool HasMatchedPackOnParmToNonPackOnArg : 1; + /// True if the candidate was found using ADL. LLVM_PREFERRED_TYPE(CallExpr::ADLCallKind) unsigned IsADLCandidate : 1; @@ -1006,6 +1011,7 @@ class Sema; OverloadCandidate() : IsSurrogate(false), IgnoreObjectArgument(false), TookAddressOfOverload(false), + HasMatchedPackOnParmToNonPackOnArg(false), IsADLCandidate(llvm::to_underlying(CallExpr::NotADL)), RewriteKind(CRK_None) {} }; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9a9998b114e0f..4d6e02fe2956e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10169,7 +10169,8 @@ class Sema final : public SemaBase { ADLCallKind IsADLCandidate = ADLCallKind::NotADL, ConversionSequenceList EarlyConversions = {}, OverloadCandidateParamOrder PO = {}, - bool AggregateCandidateDeduction = false); + bool AggregateCandidateDeduction = false, + bool HasMatchedPackOnParmToNonPackOnArg = false); /// Add all of the function declarations in the given function set to /// the overload candidate set. @@ -10204,7 +10205,8 @@ class Sema final : public SemaBase { bool SuppressUserConversions = false, bool PartialOverloading = false, ConversionSequenceList EarlyConversions = {}, - OverloadCandidateParamOrder PO = {}); + OverloadCandidateParamOrder PO = {}, + bool HasMatchedPackOnParmToNonPackOnArg = false); /// Add a C++ member function template as a candidate to the candidate /// set, using template argument deduction to produce an appropriate member @@ -10250,7 +10252,8 @@ class Sema final : public SemaBase { CXXConversionDecl *Conversion, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, Expr *From, QualType ToType, OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit, - bool AllowExplicit, bool AllowResultConversion = true); + bool AllowExplicit, bool AllowResultConversion = true, + bool HasMatchedPackOnParmToNonPackOnArg = false); /// Adds a conversion function template specialization /// candidate to the overload set, using template argument deduction @@ -11678,7 +11681,8 @@ class Sema final : public SemaBase { SourceLocation RAngleLoc, unsigned ArgumentPackIndex, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, - CheckTemplateArgumentKind CTAK); + CheckTemplateArgumentKind CTAK, bool PartialOrdering, + bool *MatchedPackOnParmToNonPackOnArg); /// Check that the given template arguments can be provided to /// the given template, converting the arguments along the way. @@ -11725,7 +11729,8 @@ class Sema final : public SemaBase { SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, bool UpdateArgsWithConversions = true, - bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false); + bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false, + bool *MatchedPackOnParmToNonPackOnArg = nullptr); bool CheckTemplateTypeArgument( TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg, @@ -11759,7 +11764,9 @@ class Sema final : public SemaBase { /// It returns true if an error occurred, and false otherwise. bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, TemplateParameterList *Params, - TemplateArgumentLoc &Arg, bool IsDeduced); + TemplateArgumentLoc &Arg, + bool PartialOrdering, + bool *MatchedPackOnParmToNonPackOnArg); void NoteTemplateLocation(const NamedDecl &Decl, std::optional ParamRange = {}); @@ -12270,8 +12277,8 @@ class Sema final : public SemaBase { SmallVectorImpl &Deduced, unsigned NumExplicitlySpecified, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, - SmallVectorImpl const *OriginalCallArgs = nullptr, - bool PartialOverloading = false, + SmallVectorImpl const *OriginalCallArgs, + bool PartialOverloading, bool PartialOrdering, llvm::function_ref CheckNonDependent = [] { return false; }); /// Perform template argument deduction from a function call @@ -12305,7 +12312,8 @@ class Sema final : public SemaBase { TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, bool PartialOverloading, bool AggregateDeductionCandidate, - QualType ObjectType, Expr::Classification ObjectClassification, + bool PartialOrdering, QualType ObjectType, + Expr::Classification ObjectClassification, llvm::function_ref)> CheckNonDependent); /// Deduce template arguments when taking the address of a function @@ -12458,8 +12466,9 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &Info); bool isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *PParam, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced); + TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, + bool PartialOrdering, bool *MatchedPackOnParmToNonPackOnArg); /// Mark which template parameters are used in a given expression. /// @@ -12768,6 +12777,9 @@ class Sema final : public SemaBase { /// We are instantiating a type alias template declaration. TypeAliasTemplateInstantiation, + + /// We are performing partial ordering for template template parameters. + PartialOrderingTTP, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -12989,6 +13001,12 @@ class Sema final : public SemaBase { TemplateDecl *Entity, BuildingDeductionGuidesTag, SourceRange InstantiationRange = SourceRange()); + struct PartialOrderingTTP {}; + /// \brief Note that we are partial ordering template template parameters. + InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc, + PartialOrderingTTP, TemplateDecl *PArg, + SourceRange InstantiationRange = SourceRange()); + /// Note that we have finished instantiating this template. void Clear(); @@ -13450,7 +13468,8 @@ class Sema final : public SemaBase { bool InstantiateClassTemplateSpecialization( SourceLocation PointOfInstantiation, ClassTemplateSpecializationDecl *ClassTemplateSpec, - TemplateSpecializationKind TSK, bool Complain = true); + TemplateSpecializationKind TSK, bool Complain = true, + bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false); /// Instantiates the definitions of all of the member /// of the given class, which is an instantiation of a class template diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h index 28b014fd84e4b..9c12eef5c42a0 100644 --- a/clang/include/clang/Sema/TemplateDeduction.h +++ b/clang/include/clang/Sema/TemplateDeduction.h @@ -51,6 +51,11 @@ class TemplateDeductionInfo { /// Have we suppressed an error during deduction? bool HasSFINAEDiagnostic = false; + /// Have we matched any packs on the parameter side, versus any non-packs on + /// the argument side, in a context where the opposite matching is also + /// allowed? + bool MatchedPackOnParmToNonPackOnArg = false; + /// The template parameter depth for which we're performing deduction. unsigned DeducedDepth; @@ -87,6 +92,14 @@ class TemplateDeductionInfo { return DeducedDepth; } + bool hasMatchedPackOnParmToNonPackOnArg() const { + return MatchedPackOnParmToNonPackOnArg; + } + + void setMatchedPackOnParmToNonPackOnArg() { + MatchedPackOnParmToNonPackOnArg = true; + } + /// Get the number of explicitly-specified arguments. unsigned getNumExplicitArgs() const { return ExplicitArgs; diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 30dfa5481d070..1ea4a2e9e88cf 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -459,6 +459,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "BuildingDeductionGuides"; case CodeSynthesisContext::TypeAliasTemplateInstantiation: return "TypeAliasTemplateInstantiation"; + case CodeSynthesisContext::PartialOrderingTTP: + return "PartialOrderingTTP"; } return ""; } diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index e18e3c197383e..2ed8d3608d49e 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3675,7 +3675,9 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, TemplateArgumentLoc Arg(TemplateArgument(StringLit), StringLit); if (CheckTemplateArgument( Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(), - 0, SugaredChecked, CanonicalChecked, CTAK_Specified) || + 0, SugaredChecked, CanonicalChecked, CTAK_Specified, + /*PartialOrdering=*/false, + /*MatchedPackOnParmToNonPackOnArg=*/nullptr) || Trap.hasErrorOccurred()) IsTemplate = false; } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 23056ca5deba3..6ae9c51c06b31 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6917,7 +6917,8 @@ void Sema::AddOverloadCandidate( OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit, bool AllowExplicitConversions, ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions, - OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) { + OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction, + bool HasMatchedPackOnParmToNonPackOnArg) { const FunctionProtoType *Proto = dyn_cast(Function->getType()->getAs()); assert(Proto && "Functions without a prototype cannot be overloaded"); @@ -6936,7 +6937,8 @@ void Sema::AddOverloadCandidate( AddMethodCandidate(Method, FoundDecl, Method->getParent(), QualType(), Expr::Classification::makeSimpleLValue(), Args, CandidateSet, SuppressUserConversions, - PartialOverloading, EarlyConversions, PO); + PartialOverloading, EarlyConversions, PO, + HasMatchedPackOnParmToNonPackOnArg); return; } // We treat a constructor like a non-member function, since its object @@ -6979,6 +6981,8 @@ void Sema::AddOverloadCandidate( CandidateSet.getRewriteInfo().getRewriteKind(Function, PO); Candidate.IsADLCandidate = llvm::to_underlying(IsADLCandidate); Candidate.ExplicitCallArguments = Args.size(); + Candidate.HasMatchedPackOnParmToNonPackOnArg = + HasMatchedPackOnParmToNonPackOnArg; // Explicit functions are not actually candidates at all if we're not // allowing them in this context, but keep them around so we can point @@ -7521,16 +7525,13 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType, } } -void -Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, - CXXRecordDecl *ActingContext, QualType ObjectType, - Expr::Classification ObjectClassification, - ArrayRef Args, - OverloadCandidateSet &CandidateSet, - bool SuppressUserConversions, - bool PartialOverloading, - ConversionSequenceList EarlyConversions, - OverloadCandidateParamOrder PO) { +void Sema::AddMethodCandidate( + CXXMethodDecl *Method, DeclAccessPair FoundDecl, + CXXRecordDecl *ActingContext, QualType ObjectType, + Expr::Classification ObjectClassification, ArrayRef Args, + OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, + bool PartialOverloading, ConversionSequenceList EarlyConversions, + OverloadCandidateParamOrder PO, bool HasMatchedPackOnParmToNonPackOnArg) { const FunctionProtoType *Proto = dyn_cast(Method->getType()->getAs()); assert(Proto && "Methods without a prototype cannot be overloaded"); @@ -7561,6 +7562,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, Candidate.TookAddressOfOverload = CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet; Candidate.ExplicitCallArguments = Args.size(); + Candidate.HasMatchedPackOnParmToNonPackOnArg = + HasMatchedPackOnParmToNonPackOnArg; bool IgnoreExplicitObject = (Method->isExplicitObjectMemberFunction() && @@ -7731,8 +7734,8 @@ void Sema::AddMethodTemplateCandidate( ConversionSequenceList Conversions; if (TemplateDeductionResult Result = DeduceTemplateArguments( MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info, - PartialOverloading, /*AggregateDeductionCandidate=*/false, ObjectType, - ObjectClassification, + PartialOverloading, /*AggregateDeductionCandidate=*/false, + /*PartialOrdering=*/false, ObjectType, ObjectClassification, [&](ArrayRef ParamTypes) { return CheckNonDependentConversions( MethodTmpl, ParamTypes, Args, CandidateSet, Conversions, @@ -7770,7 +7773,8 @@ void Sema::AddMethodTemplateCandidate( AddMethodCandidate(cast(Specialization), FoundDecl, ActingContext, ObjectType, ObjectClassification, Args, CandidateSet, SuppressUserConversions, PartialOverloading, - Conversions, PO); + Conversions, PO, + Info.hasMatchedPackOnParmToNonPackOnArg()); } /// Determine whether a given function template has a simple explicit specifier @@ -7816,6 +7820,7 @@ void Sema::AddTemplateOverloadCandidate( if (TemplateDeductionResult Result = DeduceTemplateArguments( FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info, PartialOverloading, AggregateCandidateDeduction, + /*PartialOrdering=*/false, /*ObjectType=*/QualType(), /*ObjectClassification=*/Expr::Classification(), [&](ArrayRef ParamTypes) { @@ -7856,7 +7861,8 @@ void Sema::AddTemplateOverloadCandidate( Specialization, FoundDecl, Args, CandidateSet, SuppressUserConversions, PartialOverloading, AllowExplicit, /*AllowExplicitConversions=*/false, IsADLCandidate, Conversions, PO, - Info.AggregateDeductionCandidateHasMismatchedArity); + Info.AggregateDeductionCandidateHasMismatchedArity, + Info.hasMatchedPackOnParmToNonPackOnArg()); } bool Sema::CheckNonDependentConversions( @@ -7978,7 +7984,8 @@ void Sema::AddConversionCandidate( CXXConversionDecl *Conversion, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, Expr *From, QualType ToType, OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit, - bool AllowExplicit, bool AllowResultConversion) { + bool AllowExplicit, bool AllowResultConversion, + bool HasMatchedPackOnParmToNonPackOnArg) { assert(!Conversion->getDescribedFunctionTemplate() && "Conversion function templates use AddTemplateConversionCandidate"); QualType ConvType = Conversion->getConversionType().getNonReferenceType(); @@ -8023,6 +8030,8 @@ void Sema::AddConversionCandidate( Candidate.FinalConversion.setAllToTypes(ToType); Candidate.Viable = true; Candidate.ExplicitCallArguments = 1; + Candidate.HasMatchedPackOnParmToNonPackOnArg = + HasMatchedPackOnParmToNonPackOnArg; // Explicit functions are not actually candidates at all if we're not // allowing them in this context, but keep them around so we can point @@ -8224,7 +8233,8 @@ void Sema::AddTemplateConversionCandidate( assert(Specialization && "Missing function template specialization?"); AddConversionCandidate(Specialization, FoundDecl, ActingDC, From, ToType, CandidateSet, AllowObjCConversionOnExplicit, - AllowExplicit, AllowResultConversion); + AllowExplicit, AllowResultConversion, + Info.hasMatchedPackOnParmToNonPackOnArg()); } void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, @@ -10576,6 +10586,10 @@ bool clang::isBetterOverloadCandidate( isa(Cand2.Function)) return isa(Cand1.Function); + if (Cand1.HasMatchedPackOnParmToNonPackOnArg != + Cand2.HasMatchedPackOnParmToNonPackOnArg) + return Cand2.HasMatchedPackOnParmToNonPackOnArg; + // -- F1 is a non-template function and F2 is a function template // specialization, or, if not that, bool Cand1IsSpecialization = Cand1.Function && diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 50b479052a25f..38196c5c2bc12 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5204,7 +5204,8 @@ bool Sema::CheckTemplateArgument( unsigned ArgumentPackIndex, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, - CheckTemplateArgumentKind CTAK) { + CheckTemplateArgumentKind CTAK, bool PartialOrdering, + bool *MatchedPackOnParmToNonPackOnArg) { // Check template type parameters. if (TemplateTypeParmDecl *TTP = dyn_cast(Param)) return CheckTemplateTypeArgument(TTP, Arg, SugaredConverted, @@ -5419,8 +5420,8 @@ bool Sema::CheckTemplateArgument( case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: - if (CheckTemplateTemplateArgument(TempParm, Params, Arg, - /*IsDeduced=*/CTAK != CTAK_Specified)) + if (CheckTemplateTemplateArgument(TempParm, Params, Arg, PartialOrdering, + MatchedPackOnParmToNonPackOnArg)) return true; SugaredConverted.push_back(Arg.getArgument()); @@ -5494,7 +5495,7 @@ bool Sema::CheckTemplateArgumentList( SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied, - bool PartialOrderingTTP) { + bool PartialOrderingTTP, bool *MatchedPackOnParmToNonPackOnArg) { if (ConstraintsNotSatisfied) *ConstraintsNotSatisfied = false; @@ -5508,7 +5509,7 @@ bool Sema::CheckTemplateArgumentList( SourceLocation RAngleLoc = NewArgs.getRAngleLoc(); - // C++ [temp.arg]p1: + // C++23 [temp.arg.general]p1: // [...] The type and form of each template-argument specified in // a template-id shall match the type and form specified for the // corresponding parameter declared by the template in its @@ -5527,8 +5528,7 @@ bool Sema::CheckTemplateArgumentList( DefaultArgs && ParamIdx >= DefaultArgs.StartPos) { // All written arguments should have been consumed by this point. assert(ArgIdx == NumArgs && "bad default argument deduction"); - // FIXME: Don't ignore parameter packs. - if (ParamIdx == DefaultArgs.StartPos && !(*Param)->isParameterPack()) { + if (ParamIdx == DefaultArgs.StartPos) { assert(Param + DefaultArgs.Args.size() <= ParamEnd); // Default arguments from a DeducedTemplateName are already converted. for (const TemplateArgument &DefArg : DefaultArgs.Args) { @@ -5570,60 +5570,69 @@ bool Sema::CheckTemplateArgumentList( } if (ArgIdx < NumArgs) { - // Check the template argument we were given. - if (CheckTemplateArgument(*Param, NewArgs[ArgIdx], Template, TemplateLoc, - RAngleLoc, SugaredArgumentPack.size(), - SugaredConverted, CanonicalConverted, - CTAK_Specified)) - return true; - - CanonicalConverted.back().setIsDefaulted( - clang::isSubstitutedDefaultArgument( - Context, NewArgs[ArgIdx].getArgument(), *Param, - CanonicalConverted, Params->getDepth())); - - bool PackExpansionIntoNonPack = - NewArgs[ArgIdx].getArgument().isPackExpansion() && - (!(*Param)->isTemplateParameterPack() || getExpandedPackSize(*Param)); - // CWG1430: Don't diagnose this pack expansion when partial - // ordering template template parameters. Some uses of the template could - // be valid, and invalid uses will be diagnosed later during - // instantiation. - if (PackExpansionIntoNonPack && !PartialOrderingTTP && - (isa(Template) || - isa(Template))) { - // CWG1430: we have a pack expansion as an argument to an - // alias template, and it's not part of a parameter pack. This - // can't be canonicalized, so reject it now. - // As for concepts - we cannot normalize constraints where this - // situation exists. - Diag(NewArgs[ArgIdx].getLocation(), - diag::err_template_expansion_into_fixed_list) - << (isa(Template) ? 1 : 0) - << NewArgs[ArgIdx].getSourceRange(); - NoteTemplateParameterLocation(**Param); - return true; + TemplateArgumentLoc &ArgLoc = NewArgs[ArgIdx]; + bool NonPackParameter = + !(*Param)->isTemplateParameterPack() || getExpandedPackSize(*Param); + bool ArgIsExpansion = ArgLoc.getArgument().isPackExpansion(); + + if (ArgIsExpansion && PartialOrderingTTP) { + SmallVector Args(ParamEnd - Param); + for (TemplateParameterList::iterator First = Param; Param != ParamEnd; + ++Param) { + TemplateArgument &Arg = Args[Param - First]; + Arg = ArgLoc.getArgument(); + if (!(*Param)->isTemplateParameterPack() || + getExpandedPackSize(*Param)) + Arg = Arg.getPackExpansionPattern(); + TemplateArgumentLoc NewArgLoc(Arg, ArgLoc.getLocInfo()); + if (CheckTemplateArgument(*Param, NewArgLoc, Template, TemplateLoc, + RAngleLoc, SugaredArgumentPack.size(), + SugaredConverted, CanonicalConverted, + CTAK_Specified, /*PartialOrdering=*/false, + MatchedPackOnParmToNonPackOnArg)) + return true; + Arg = NewArgLoc.getArgument(); + CanonicalConverted.back().setIsDefaulted( + clang::isSubstitutedDefaultArgument(Context, Arg, *Param, + CanonicalConverted, + Params->getDepth())); + } + ArgLoc = + TemplateArgumentLoc(TemplateArgument::CreatePackCopy(Context, Args), + ArgLoc.getLocInfo()); + } else { + if (CheckTemplateArgument(*Param, ArgLoc, Template, TemplateLoc, + RAngleLoc, SugaredArgumentPack.size(), + SugaredConverted, CanonicalConverted, + CTAK_Specified, /*PartialOrdering=*/false, + MatchedPackOnParmToNonPackOnArg)) + return true; + CanonicalConverted.back().setIsDefaulted( + clang::isSubstitutedDefaultArgument(Context, ArgLoc.getArgument(), + *Param, CanonicalConverted, + Params->getDepth())); + if (ArgIsExpansion && NonPackParameter) { + // CWG1430/CWG2686: we have a pack expansion as an argument to an + // alias template or concept, and it's not part of a parameter pack. + // This can't be canonicalized, so reject it now. + if (isa(Template)) { + Diag(ArgLoc.getLocation(), + diag::err_template_expansion_into_fixed_list) + << (isa(Template) ? 1 : 0) + << ArgLoc.getSourceRange(); + NoteTemplateParameterLocation(**Param); + return true; + } + } } // We're now done with this argument. ++ArgIdx; - if ((*Param)->isTemplateParameterPack()) { - // The template parameter was a template parameter pack, so take the - // deduced argument and place it on the argument pack. Note that we - // stay on the same template parameter so that we can deduce more - // arguments. - SugaredArgumentPack.push_back(SugaredConverted.pop_back_val()); - CanonicalArgumentPack.push_back(CanonicalConverted.pop_back_val()); - } else { - // Move to the next template parameter. - ++Param; - } + if (ArgIsExpansion && (PartialOrderingTTP || NonPackParameter)) { + // Directly convert the remaining arguments, because we don't know what + // parameters they'll match up with. - // If we just saw a pack expansion into a non-pack, then directly convert - // the remaining arguments, because we don't know what parameters they'll - // match up with. - if (PackExpansionIntoNonPack) { if (!SugaredArgumentPack.empty()) { // If we were part way through filling in an expanded parameter pack, // fall back to just producing individual arguments. @@ -5649,6 +5658,17 @@ bool Sema::CheckTemplateArgumentList( return false; } + if ((*Param)->isTemplateParameterPack()) { + // The template parameter was a template parameter pack, so take the + // deduced argument and place it on the argument pack. Note that we + // stay on the same template parameter so that we can deduce more + // arguments. + SugaredArgumentPack.push_back(SugaredConverted.pop_back_val()); + CanonicalArgumentPack.push_back(CanonicalConverted.pop_back_val()); + } else { + // Move to the next template parameter. + ++Param; + } continue; } @@ -5732,7 +5752,8 @@ bool Sema::CheckTemplateArgumentList( // Check the default template argument. if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc, RAngleLoc, 0, SugaredConverted, CanonicalConverted, - CTAK_Specified)) + CTAK_Specified, /*PartialOrdering=*/false, + /*MatchedPackOnParmToNonPackOnArg=*/nullptr)) return true; SugaredConverted.back().setIsDefaulted(true); @@ -5753,8 +5774,9 @@ bool Sema::CheckTemplateArgumentList( // pack expansions; they might be empty. This can happen even if // PartialTemplateArgs is false (the list of arguments is complete but // still dependent). - if (ArgIdx < NumArgs && CurrentInstantiationScope && - CurrentInstantiationScope->getPartiallySubstitutedPack()) { + if (PartialOrderingTTP || + (CurrentInstantiationScope && + CurrentInstantiationScope->getPartiallySubstitutedPack())) { while (ArgIdx < NumArgs && NewArgs[ArgIdx].getArgument().isPackExpansion()) { const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument(); @@ -7321,10 +7343,10 @@ static void DiagnoseTemplateParameterListArityMismatch( Sema &S, TemplateParameterList *New, TemplateParameterList *Old, Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc); -bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, - TemplateParameterList *Params, - TemplateArgumentLoc &Arg, - bool IsDeduced) { +bool Sema::CheckTemplateTemplateArgument( + TemplateTemplateParmDecl *Param, TemplateParameterList *Params, + TemplateArgumentLoc &Arg, bool PartialOrdering, + bool *MatchedPackOnParmToNonPackOnArg) { TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern(); auto [Template, DefaultArgs] = Name.getTemplateDeclAndDefaultArgs(); if (!Template) { @@ -7359,64 +7381,47 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, << Template; } + if (!getLangOpts().RelaxedTemplateTemplateArgs) + return !TemplateParameterListsAreEqual( + Template->getTemplateParameters(), Params, /*Complain=*/true, + TPL_TemplateTemplateArgumentMatch, Arg.getLocation()); + // C++1z [temp.arg.template]p3: (DR 150) // A template-argument matches a template template-parameter P when P // is at least as specialized as the template-argument A. - if (getLangOpts().RelaxedTemplateTemplateArgs) { - // Quick check for the common case: - // If P contains a parameter pack, then A [...] matches P if each of A's - // template parameters matches the corresponding template parameter in - // the template-parameter-list of P. - if (TemplateParameterListsAreEqual( - Template->getTemplateParameters(), Params, false, - TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) && - // If the argument has no associated constraints, then the parameter is - // definitely at least as specialized as the argument. - // Otherwise - we need a more thorough check. - !Template->hasAssociatedConstraints()) - return false; - - if (isTemplateTemplateParameterAtLeastAsSpecializedAs( - Params, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) { - // P2113 - // C++20[temp.func.order]p2 - // [...] If both deductions succeed, the partial ordering selects the - // more constrained template (if one exists) as determined below. - SmallVector ParamsAC, TemplateAC; - Params->getAssociatedConstraints(ParamsAC); - // C++2a[temp.arg.template]p3 - // [...] In this comparison, if P is unconstrained, the constraints on A - // are not considered. - if (ParamsAC.empty()) - return false; + if (!isTemplateTemplateParameterAtLeastAsSpecializedAs( + Params, Param, Template, DefaultArgs, Arg.getLocation(), + PartialOrdering, MatchedPackOnParmToNonPackOnArg)) + return true; + // P2113 + // C++20[temp.func.order]p2 + // [...] If both deductions succeed, the partial ordering selects the + // more constrained template (if one exists) as determined below. + SmallVector ParamsAC, TemplateAC; + Params->getAssociatedConstraints(ParamsAC); + // C++20[temp.arg.template]p3 + // [...] In this comparison, if P is unconstrained, the constraints on A + // are not considered. + if (ParamsAC.empty()) + return false; - Template->getAssociatedConstraints(TemplateAC); + Template->getAssociatedConstraints(TemplateAC); - bool IsParamAtLeastAsConstrained; - if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, - IsParamAtLeastAsConstrained)) - return true; - if (!IsParamAtLeastAsConstrained) { - Diag(Arg.getLocation(), - diag::err_template_template_parameter_not_at_least_as_constrained) - << Template << Param << Arg.getSourceRange(); - Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; - Diag(Template->getLocation(), diag::note_entity_declared_at) - << Template; - MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, - TemplateAC); - return true; - } - return false; - } - // FIXME: Produce better diagnostics for deduction failures. + bool IsParamAtLeastAsConstrained; + if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, + IsParamAtLeastAsConstrained)) + return true; + if (!IsParamAtLeastAsConstrained) { + Diag(Arg.getLocation(), + diag::err_template_template_parameter_not_at_least_as_constrained) + << Template << Param << Arg.getSourceRange(); + Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; + Diag(Template->getLocation(), diag::note_entity_declared_at) << Template; + MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, + TemplateAC); + return true; } - - return !TemplateParameterListsAreEqual(Template->getTemplateParameters(), - Params, - true, - TPL_TemplateTemplateArgumentMatch, - Arg.getLocation()); + return false; } static Sema::SemaDiagnosticBuilder noteLocation(Sema &S, const NamedDecl &Decl, @@ -9816,11 +9821,14 @@ DeclResult Sema::ActOnExplicitInstantiation( // Check that the template argument list is well-formed for this // template. + bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false; SmallVector SugaredConverted, CanonicalConverted; - if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, TemplateArgs, - /*DefaultArgs=*/{}, false, SugaredConverted, - CanonicalConverted, - /*UpdateArgsWithConversions=*/true)) + if (CheckTemplateArgumentList( + ClassTemplate, TemplateNameLoc, TemplateArgs, + /*DefaultArgs=*/{}, false, SugaredConverted, CanonicalConverted, + /*UpdateArgsWithConversions=*/true, + /*ConstraintsNotSatisfied=*/nullptr, /*PartialOrderingTTP=*/false, + &PrimaryHasMatchedPackOnParmToNonPackOnArg)) return true; // Find the class template specialization declaration that @@ -9941,7 +9949,9 @@ DeclResult Sema::ActOnExplicitInstantiation( = cast_or_null( Specialization->getDefinition()); if (!Def) - InstantiateClassTemplateSpecialization(TemplateNameLoc, Specialization, TSK); + InstantiateClassTemplateSpecialization( + TemplateNameLoc, Specialization, TSK, + /*Complain=*/true, PrimaryHasMatchedPackOnParmToNonPackOnArg); else if (TSK == TSK_ExplicitInstantiationDefinition) { MarkVTableUsed(TemplateNameLoc, Specialization, true); Specialization->setPointOfInstantiation(Def->getPointOfInstantiation()); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 1c1f6e30ab7b8..7882d7a755d34 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -145,7 +145,9 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( PartialOrderingKind POK, bool DeducedFromArrayBound, bool *HasDeducedAnyParam); -enum class PackFold { ParameterToArgument, ArgumentToParameter }; +/// What directions packs are allowed to match non-packs. +enum class PackFold { ParameterToArgument, ArgumentToParameter, Both }; + static TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, ArrayRef Ps, @@ -1715,7 +1717,21 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, Deduced[Index], NewDeduced); if (Result.isNull()) { - Info.Param = cast(TemplateParams->getParam(Index)); + // We can also get inconsistencies when matching NTTP type. + switch (NamedDecl *Param = TemplateParams->getParam(Index); + Param->getKind()) { + case Decl::TemplateTypeParm: + Info.Param = cast(Param); + break; + case Decl::NonTypeTemplateParm: + Info.Param = cast(Param); + break; + case Decl::TemplateTemplateParm: + Info.Param = cast(Param); + break; + default: + llvm_unreachable("unexpected kind"); + } Info.FirstArg = Deduced[Index]; Info.SecondArg = NewDeduced; return TemplateDeductionResult::Inconsistent; @@ -2555,8 +2571,31 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, P.getAsExpr())) { switch (A.getKind()) { + case TemplateArgument::Expression: { + const Expr *E = A.getAsExpr(); + // When checking NTTP, if either the parameter or the argument is + // dependent, as there would be otherwise nothing to deduce, we force + // the argument to the parameter type using this dependent implicit + // cast, in order to maintain invariants. Now we can deduce the + // resulting type from the original type, and deduce the original type + // against the parameter we are checking. + if (const auto *ICE = dyn_cast(E); + ICE && ICE->getCastKind() == clang::CK_Dependent) { + E = ICE->getSubExpr(); + if (auto Result = DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, ICE->getType(), E->getType(), Info, + Deduced, TDF_SkipNonDependent, + PartialOrdering ? PartialOrderingKind::NonCall + : PartialOrderingKind::None, + /*DeducedFromArrayBound=*/false, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; + } + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, DeducedTemplateArgument(A), E->getType(), + Info, PartialOrdering, Deduced, HasDeducedAnyParam); + } case TemplateArgument::Integral: - case TemplateArgument::Expression: case TemplateArgument::StructuralValue: return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(A), @@ -2645,50 +2684,72 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, SmallVectorImpl &Deduced, bool NumberOfArgumentsMustMatch, bool PartialOrdering, PackFold PackFold, bool *HasDeducedAnyParam) { - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Ps, As); + bool FoldPackParameter = PackFold == PackFold::ParameterToArgument || + PackFold == PackFold::Both, + FoldPackArgument = PackFold == PackFold::ArgumentToParameter || + PackFold == PackFold::Both; + // C++0x [temp.deduct.type]p9: // If the template argument list of P contains a pack expansion that is not // the last template argument, the entire template argument list is a // non-deduced context. - if (hasPackExpansionBeforeEnd(Ps)) + if (FoldPackParameter && hasPackExpansionBeforeEnd(Ps)) return TemplateDeductionResult::Success; // C++0x [temp.deduct.type]p9: // If P has a form that contains or , then each argument Pi of the // respective template argument list P is compared with the corresponding // argument Ai of the corresponding template argument list of A. - unsigned ArgIdx = 0, ParamIdx = 0; - for (; hasTemplateArgumentForDeduction(Ps, ParamIdx); ++ParamIdx) { - const TemplateArgument &P = Ps[ParamIdx]; - if (!P.isPackExpansion()) { + for (unsigned ArgIdx = 0, ParamIdx = 0; /**/; /**/) { + if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) + return !FoldPackParameter && hasTemplateArgumentForDeduction(As, ArgIdx) + ? TemplateDeductionResult::MiscellaneousDeductionFailure + : TemplateDeductionResult::Success; + + if (!Ps[ParamIdx].isPackExpansion()) { // The simple case: deduce template arguments by matching Pi and Ai. // Check whether we have enough arguments. if (!hasTemplateArgumentForDeduction(As, ArgIdx)) - return NumberOfArgumentsMustMatch + return !FoldPackArgument && NumberOfArgumentsMustMatch ? TemplateDeductionResult::MiscellaneousDeductionFailure : TemplateDeductionResult::Success; - // C++1z [temp.deduct.type]p9: - // During partial ordering, if Ai was originally a pack expansion [and] - // Pi is not a pack expansion, template argument deduction fails. - if (As[ArgIdx].isPackExpansion()) - return TemplateDeductionResult::MiscellaneousDeductionFailure; + if (As[ArgIdx].isPackExpansion()) { + // C++1z [temp.deduct.type]p9: + // During partial ordering, if Ai was originally a pack expansion + // [and] Pi is not a pack expansion, template argument deduction + // fails. + if (!FoldPackArgument) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + + TemplateArgument Pattern = As[ArgIdx].getPackExpansionPattern(); + for (;;) { + // Deduce template parameters from the pattern. + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Ps[ParamIdx], Pattern, Info, + PartialOrdering, Deduced, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - // Perform deduction for this Pi/Ai pair. - TemplateArgument Pi = P, Ai = As[ArgIdx]; - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Pi, Ai); - if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, - PartialOrdering, Deduced, - HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; + ++ParamIdx; + if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) + return TemplateDeductionResult::Success; + if (Ps[ParamIdx].isPackExpansion()) + break; + } + } else { + // Perform deduction for this Pi/Ai pair. + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Ps[ParamIdx], As[ArgIdx], Info, + PartialOrdering, Deduced, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - // Move to the next argument. - ++ArgIdx; - continue; + ++ArgIdx; + ++ParamIdx; + continue; + } } // The parameter is a pack expansion. @@ -2698,7 +2759,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // each remaining argument in the template argument list of A. Each // comparison deduces template arguments for subsequent positions in the // template parameter packs expanded by Pi. - TemplateArgument Pattern = P.getPackExpansionPattern(); + TemplateArgument Pattern = Ps[ParamIdx].getPackExpansionPattern(); // Prepare to deduce the packs within the pattern. PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern); @@ -2709,13 +2770,16 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, for (; hasTemplateArgumentForDeduction(As, ArgIdx) && PackScope.hasNextElement(); ++ArgIdx) { - TemplateArgument Pi = Pattern, Ai = As[ArgIdx]; - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Pi, Ai); + if (!As[ArgIdx].isPackExpansion()) { + if (!FoldPackParameter) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + if (FoldPackArgument) + Info.setMatchedPackOnParmToNonPackOnArg(); + } // Deduce template arguments from the pattern. - if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, - PartialOrdering, Deduced, - HasDeducedAnyParam); + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Pattern, As[ArgIdx], Info, PartialOrdering, + Deduced, HasDeducedAnyParam); Result != TemplateDeductionResult::Success) return Result; @@ -2724,12 +2788,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // Build argument packs for each of the parameter packs expanded by this // pack expansion. - if (auto Result = PackScope.finish(); - Result != TemplateDeductionResult::Success) - return Result; + return PackScope.finish(); } - - return TemplateDeductionResult::Success; } TemplateDeductionResult Sema::DeduceTemplateArguments( @@ -2898,7 +2958,7 @@ Sema::getIdentityTemplateArgumentLoc(NamedDecl *TemplateParm, /// fully-converted template arguments. static bool ConvertDeducedTemplateArgument( Sema &S, NamedDecl *Param, DeducedTemplateArgument Arg, NamedDecl *Template, - TemplateDeductionInfo &Info, bool IsDeduced, + TemplateDeductionInfo &Info, bool IsDeduced, bool PartialOrdering, SmallVectorImpl &SugaredOutput, SmallVectorImpl &CanonicalOutput) { auto ConvertArg = [&](DeducedTemplateArgument Arg, @@ -2909,15 +2969,20 @@ static bool ConvertDeducedTemplateArgument( TemplateArgumentLoc ArgLoc = S.getTrivialTemplateArgumentLoc( Arg, QualType(), Info.getLocation(), Param); + bool MatchedPackOnParmToNonPackOnArg = false; // Check the template argument, converting it as necessary. - return S.CheckTemplateArgument( + auto Res = S.CheckTemplateArgument( Param, ArgLoc, Template, Template->getLocation(), Template->getSourceRange().getEnd(), ArgumentPackIndex, SugaredOutput, CanonicalOutput, IsDeduced ? (Arg.wasDeducedFromArrayBound() ? Sema::CTAK_DeducedFromArrayBound : Sema::CTAK_Deduced) - : Sema::CTAK_Specified); + : Sema::CTAK_Specified, + PartialOrdering, &MatchedPackOnParmToNonPackOnArg); + if (MatchedPackOnParmToNonPackOnArg) + Info.setMatchedPackOnParmToNonPackOnArg(); + return Res; }; if (Arg.getKind() == TemplateArgument::Pack) { @@ -3000,9 +3065,9 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments( SmallVectorImpl &Deduced, TemplateDeductionInfo &Info, SmallVectorImpl &SugaredBuilder, - SmallVectorImpl &CanonicalBuilder, - LocalInstantiationScope *CurrentInstantiationScope = nullptr, - unsigned NumAlreadyConverted = 0, bool *IsIncomplete = nullptr) { + SmallVectorImpl &CanonicalBuilder, bool PartialOrdering, + LocalInstantiationScope *CurrentInstantiationScope, + unsigned NumAlreadyConverted, bool *IsIncomplete) { TemplateParameterList *TemplateParams = Template->getTemplateParameters(); for (unsigned I = 0, N = TemplateParams->size(); I != N; ++I) { @@ -3045,8 +3110,8 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments( // We may have deduced this argument, so it still needs to be // checked and converted. if (ConvertDeducedTemplateArgument(S, Param, Deduced[I], Template, Info, - IsDeduced, SugaredBuilder, - CanonicalBuilder)) { + IsDeduced, PartialOrdering, + SugaredBuilder, CanonicalBuilder)) { Info.Param = makeTemplateParameter(Param); // FIXME: These template arguments are temporary. Free them! Info.reset( @@ -3112,7 +3177,9 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments( // Check whether we can actually use the default argument. if (S.CheckTemplateArgument( Param, DefArg, TD, TD->getLocation(), TD->getSourceRange().getEnd(), - 0, SugaredBuilder, CanonicalBuilder, Sema::CTAK_Specified)) { + /*ArgumentPackIndex=*/0, SugaredBuilder, CanonicalBuilder, + Sema::CTAK_Specified, /*PartialOrdering=*/false, + /*MatchedPackOnParmToNonPackOnArg=*/nullptr)) { Info.Param = makeTemplateParameter( const_cast(TemplateParams->getParam(I))); // FIXME: These template arguments are temporary. Free them! @@ -3220,7 +3287,9 @@ FinishTemplateArgumentDeduction( SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( S, Partial, IsPartialOrdering, Deduced, Info, SugaredBuilder, - CanonicalBuilder); + CanonicalBuilder, IsPartialOrdering, + /*CurrentInstantiationScope=*/nullptr, /*NumAlreadyConverted=*/0, + /*IsIncomplete=*/nullptr); Result != TemplateDeductionResult::Success) return Result; @@ -3261,16 +3330,20 @@ FinishTemplateArgumentDeduction( return TemplateDeductionResult::SubstitutionFailure; } + bool MatchedPackOnParmToNonPackOnArg = false; bool ConstraintsNotSatisfied; SmallVector SugaredConvertedInstArgs, CanonicalConvertedInstArgs; if (S.CheckTemplateArgumentList( Template, Partial->getLocation(), InstArgs, /*DefaultArgs=*/{}, false, SugaredConvertedInstArgs, CanonicalConvertedInstArgs, - /*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied)) + /*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied, + /*PartialOrderingTTP=*/false, &MatchedPackOnParmToNonPackOnArg)) return ConstraintsNotSatisfied ? TemplateDeductionResult::ConstraintsNotSatisfied : TemplateDeductionResult::SubstitutionFailure; + if (MatchedPackOnParmToNonPackOnArg) + Info.setMatchedPackOnParmToNonPackOnArg(); TemplateParameterList *TemplateParams = Template->getTemplateParameters(); for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { @@ -3308,7 +3381,6 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Template)); @@ -3317,28 +3389,69 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // explicitly specified, template argument deduction fails. SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - S, Template, /*IsDeduced*/ PartialOrdering, Deduced, Info, - SugaredBuilder, CanonicalBuilder, + S, Template, /*IsDeduced=*/PartialOrdering, Deduced, Info, + SugaredBuilder, CanonicalBuilder, PartialOrdering, /*CurrentInstantiationScope=*/nullptr, - /*NumAlreadyConverted=*/0U); + /*NumAlreadyConverted=*/0U, /*IsIncomplete=*/nullptr); Result != TemplateDeductionResult::Success) return Result; // Check that we produced the correct argument list. - TemplateParameterList *TemplateParams = Template->getTemplateParameters(); - for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { - TemplateArgument InstArg = CanonicalBuilder[I]; - if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg, PartialOrdering, - /*PackExpansionMatchesPack=*/true)) { - Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); - Info.FirstArg = TemplateArgs[I]; - Info.SecondArg = InstArg; - return TemplateDeductionResult::NonDeducedMismatch; + SmallVector, 4> PsStack{TemplateArgs}, + AsStack{CanonicalBuilder}; + for (;;) { + auto take = [](SmallVectorImpl> &Stack) + -> std::tuple &, TemplateArgument> { + while (!Stack.empty()) { + auto &Xs = Stack.back(); + if (Xs.empty()) { + Stack.pop_back(); + continue; + } + auto &X = Xs.front(); + if (X.getKind() == TemplateArgument::Pack) { + Stack.emplace_back(X.getPackAsArray()); + Xs = Xs.drop_front(); + continue; + } + assert(!X.isNull()); + return {Xs, X}; + } + static constexpr ArrayRef None; + return {const_cast &>(None), + TemplateArgument()}; + }; + auto [Ps, P] = take(PsStack); + auto [As, A] = take(AsStack); + if (P.isNull() && A.isNull()) + break; + TemplateArgument PP = P.isPackExpansion() ? P.getPackExpansionPattern() : P, + PA = A.isPackExpansion() ? A.getPackExpansionPattern() : A; + if (!isSameTemplateArg(S.Context, PP, PA, /*PartialOrdering=*/false)) { + if (!P.isPackExpansion() && !A.isPackExpansion()) { + Info.Param = + makeTemplateParameter(Template->getTemplateParameters()->getParam( + (PsStack.empty() ? TemplateArgs.end() + : PsStack.front().begin()) - + TemplateArgs.begin())); + Info.FirstArg = P; + Info.SecondArg = A; + return TemplateDeductionResult::NonDeducedMismatch; + } + if (P.isPackExpansion()) { + Ps = Ps.drop_front(); + continue; + } + if (A.isPackExpansion()) { + As = As.drop_front(); + continue; + } } + Ps = Ps.drop_front(P.isPackExpansion() ? 0 : 1); + As = As.drop_front(A.isPackExpansion() && !P.isPackExpansion() ? 0 : 1); } - - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; + assert(PsStack.empty()); + assert(AsStack.empty()); if (!PartialOrdering) { if (auto Result = CheckDeducedArgumentConstraints( @@ -3360,7 +3473,6 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(TD)); @@ -3369,20 +3481,15 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // explicitly specified, template argument deduction fails. SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - S, TD, /*IsPartialOrdering=*/false, Deduced, Info, SugaredBuilder, - CanonicalBuilder); - Result != TemplateDeductionResult::Success) - return Result; - - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - - if (auto Result = CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, - CanonicalBuilder, Info); + S, TD, /*IsDeduced=*/false, Deduced, Info, SugaredBuilder, + CanonicalBuilder, /*PartialOrdering=*/false, + /*CurrentInstantiationScope=*/nullptr, /*NumAlreadyConverted=*/0, + /*IsIncomplete=*/nullptr); Result != TemplateDeductionResult::Success) return Result; - return TemplateDeductionResult::Success; + return ::CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, + CanonicalBuilder, Info); } /// Perform template argument deduction to determine whether the given template @@ -3429,16 +3536,20 @@ DeduceTemplateArguments(Sema &S, T *Partial, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(S, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info); }); - return Result; + + if (Result != TemplateDeductionResult::Success) + return Result; + + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + return TemplateDeductionResult::Success; } TemplateDeductionResult @@ -3494,14 +3605,18 @@ Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(*this, TD, Deduced, Info); }); - return Result; + + if (Result != TemplateDeductionResult::Success) + return Result; + + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + return TemplateDeductionResult::Success; } /// Determine whether the given type T is a simple-template-id type. @@ -3907,7 +4022,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( unsigned NumExplicitlySpecified, FunctionDecl *&Specialization, TemplateDeductionInfo &Info, SmallVectorImpl const *OriginalCallArgs, - bool PartialOverloading, llvm::function_ref CheckNonDependent) { + bool PartialOverloading, bool PartialOrdering, + llvm::function_ref CheckNonDependent) { // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( *this, Sema::ExpressionEvaluationContext::Unevaluated); @@ -3930,9 +4046,10 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( bool IsIncomplete = false; SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - *this, FunctionTemplate, /*IsDeduced*/ true, Deduced, Info, - SugaredBuilder, CanonicalBuilder, CurrentInstantiationScope, - NumExplicitlySpecified, PartialOverloading ? &IsIncomplete : nullptr); + *this, FunctionTemplate, /*IsDeduced=*/true, Deduced, Info, + SugaredBuilder, CanonicalBuilder, PartialOrdering, + CurrentInstantiationScope, NumExplicitlySpecified, + PartialOverloading ? &IsIncomplete : nullptr); Result != TemplateDeductionResult::Success) return Result; @@ -4463,7 +4580,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, TemplateDeductionInfo &Info, bool PartialOverloading, bool AggregateDeductionCandidate, - QualType ObjectType, Expr::Classification ObjectClassification, + bool PartialOrdering, QualType ObjectType, + Expr::Classification ObjectClassification, llvm::function_ref)> CheckNonDependent) { if (FunctionTemplate->isInvalidDecl()) return TemplateDeductionResult::Invalid; @@ -4678,7 +4796,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( runWithSufficientStackSpace(Info.getLocation(), [&] { Result = FinishTemplateArgumentDeduction( FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info, - &OriginalCallArgs, PartialOverloading, [&, CallingCtx]() { + &OriginalCallArgs, PartialOverloading, PartialOrdering, + [&, CallingCtx]() { ContextRAII SavedContext(*this, CallingCtx); return CheckNonDependent(ParamTypesForArgChecking); }); @@ -4790,9 +4909,10 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { - Result = FinishTemplateArgumentDeduction(FunctionTemplate, Deduced, - NumExplicitlySpecified, - Specialization, Info); + Result = FinishTemplateArgumentDeduction( + FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info, + /*OriginalCallArgs=*/nullptr, /*PartialOverloading=*/false, + /*PartialOrdering=*/true); }); if (Result != TemplateDeductionResult::Success) return Result; @@ -4972,9 +5092,10 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( FunctionDecl *ConversionSpecialized = nullptr; TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { - Result = FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0, - ConversionSpecialized, Info, - &OriginalCallArgs); + Result = FinishTemplateArgumentDeduction( + ConversionTemplate, Deduced, 0, ConversionSpecialized, Info, + &OriginalCallArgs, /*PartialOverloading=*/false, + /*PartialOrdering=*/false); }); Specialization = cast_or_null(ConversionSpecialized); return Result; @@ -5551,7 +5672,8 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( S, FTD, /*IsDeduced=*/true, Deduced, Info, SugaredBuilder, - CanonicalBuilder, /*CurrentInstantiationScope=*/nullptr, + CanonicalBuilder, /*PartialOrdering=*/true, + /*CurrentInstantiationScope=*/nullptr, /*NumAlreadyConverted=*/0, &IsIncomplete); Result != TemplateDeductionResult::Success) return Result; @@ -6141,14 +6263,23 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, return false; const auto *TST1 = cast(T1); - bool AtLeastAsSpecialized; + + Sema::SFINAETrap Trap(S); + + TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { - AtLeastAsSpecialized = - FinishTemplateArgumentDeduction( - S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), - Deduced, Info) == TemplateDeductionResult::Success; + Result = ::FinishTemplateArgumentDeduction( + S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), Deduced, + Info); }); - return AtLeastAsSpecialized; + + if (Result != TemplateDeductionResult::Success) + return false; + + if (Trap.hasErrorOccurred()) + return false; + + return true; } namespace { @@ -6386,8 +6517,9 @@ bool Sema::isMoreSpecializedThanPrimary( } bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *P, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced) { + TemplateParameterList *P, TemplateDecl *PArg, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, + bool PartialOrdering, bool *MatchedPackOnParmToNonPackOnArg) { // C++1z [temp.arg.template]p4: (DR 150) // A template template-parameter P is at least as specialized as a // template template-argument A if, given the following rewrite to two @@ -6399,6 +6531,12 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // TemplateParameterList *A = AArg->getTemplateParameters(); + Sema::InstantiatingTemplate Inst( + *this, ArgLoc, Sema::InstantiatingTemplate::PartialOrderingTTP(), PArg, + SourceRange(P->getTemplateLoc(), P->getRAngleLoc())); + if (Inst.isInvalid()) + return false; + // Given an invented class template X with the template parameter list of // A (including default arguments): // - Each function template has a single function parameter whose type is @@ -6427,22 +6565,20 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( } PArgs.clear(); - SFINAETrap Trap(*this); // C++1z [temp.arg.template]p3: // If the rewrite produces an invalid type, then P is not at least as // specialized as A. - SmallVector SugaredPArgs; - if (CheckTemplateArgumentList(AArg, Loc, PArgList, DefaultArgs, false, - SugaredPArgs, PArgs, - /*UpdateArgsWithConversions=*/true, - /*ConstraintsNotSatisfied=*/nullptr, - /*PartialOrderTTP=*/true) || - Trap.hasErrorOccurred()) + SmallVector CanonicalPArgs; + if (CheckTemplateArgumentList( + AArg, ArgLoc, PArgList, DefaultArgs, false, PArgs, CanonicalPArgs, + /*UpdateArgsWithConversions=*/true, + /*ConstraintsNotSatisfied=*/nullptr, + /*PartialOrderingTTP=*/true, MatchedPackOnParmToNonPackOnArg)) return false; } // Determine whether P1 is at least as specialized as P2. - TemplateDeductionInfo Info(Loc, A->getDepth()); + TemplateDeductionInfo Info(ArgLoc, A->getDepth()); SmallVector Deduced; Deduced.resize(A->size()); @@ -6457,29 +6593,92 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // be inverted between Ps and As. On non-deduced context, matching needs to // happen both ways, according to [temp.arg.template]p3, but this is // currently implemented as a special case elsewhere. - if (::DeduceTemplateArguments(*this, A, AArgs, PArgs, Info, Deduced, - /*NumberOfArgumentsMustMatch=*/false, - /*PartialOrdering=*/true, - IsDeduced ? PackFold::ArgumentToParameter - : PackFold::ParameterToArgument, - /*HasDeducedAnyParam=*/nullptr) != - TemplateDeductionResult::Success) + switch (::DeduceTemplateArguments( + *this, A, AArgs, PArgs, Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false, /*PartialOrdering=*/true, + PartialOrdering ? PackFold::ArgumentToParameter : PackFold::Both, + /*HasDeducedAnyParam=*/nullptr)) { + case clang::TemplateDeductionResult::Success: + if (MatchedPackOnParmToNonPackOnArg && + Info.hasMatchedPackOnParmToNonPackOnArg()) + *MatchedPackOnParmToNonPackOnArg = true; + break; + + case TemplateDeductionResult::MiscellaneousDeductionFailure: + Diag(AArg->getLocation(), diag::err_template_param_list_different_arity) + << (A->size() > P->size()) << /*isTemplateTemplateParameter=*/true + << SourceRange(A->getTemplateLoc(), P->getRAngleLoc()); return false; + case TemplateDeductionResult::NonDeducedMismatch: + Diag(AArg->getLocation(), diag::err_non_deduced_mismatch) + << Info.FirstArg << Info.SecondArg; + return false; + case TemplateDeductionResult::Inconsistent: + Diag(getAsNamedDecl(Info.Param)->getLocation(), + diag::err_inconsistent_deduction) + << Info.FirstArg << Info.SecondArg; + return false; + case TemplateDeductionResult::AlreadyDiagnosed: + return false; + + // None of these should happen for a plain deduction. + case TemplateDeductionResult::Invalid: + case TemplateDeductionResult::InstantiationDepth: + case TemplateDeductionResult::Incomplete: + case TemplateDeductionResult::IncompletePack: + case TemplateDeductionResult::Underqualified: + case TemplateDeductionResult::SubstitutionFailure: + case TemplateDeductionResult::DeducedMismatch: + case TemplateDeductionResult::DeducedMismatchNested: + case TemplateDeductionResult::TooManyArguments: + case TemplateDeductionResult::TooFewArguments: + case TemplateDeductionResult::InvalidExplicitArguments: + case TemplateDeductionResult::NonDependentConversionFailure: + case TemplateDeductionResult::ConstraintsNotSatisfied: + case TemplateDeductionResult::CUDATargetMismatch: + llvm_unreachable("Unexpected Result"); + } SmallVector DeducedArgs(Deduced.begin(), Deduced.end()); - Sema::InstantiatingTemplate Inst(*this, Info.getLocation(), AArg, DeducedArgs, - Info); - if (Inst.isInvalid()) - return false; - bool AtLeastAsSpecialized; + TemplateDeductionResult TDK; runWithSufficientStackSpace(Info.getLocation(), [&] { - AtLeastAsSpecialized = - ::FinishTemplateArgumentDeduction( - *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info) == - TemplateDeductionResult::Success; + TDK = ::FinishTemplateArgumentDeduction( + *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info); }); - return AtLeastAsSpecialized; + switch (TDK) { + case TemplateDeductionResult::Success: + return true; + + // It doesn't seem possible to get a non-deduced mismatch when partial + // ordering TTPs. + case TemplateDeductionResult::NonDeducedMismatch: + llvm_unreachable("Unexpected NonDeducedMismatch"); + + // Substitution failures should have already been diagnosed. + case TemplateDeductionResult::AlreadyDiagnosed: + case TemplateDeductionResult::SubstitutionFailure: + case TemplateDeductionResult::InstantiationDepth: + return false; + + // None of these should happen when just converting deduced arguments. + case TemplateDeductionResult::Invalid: + case TemplateDeductionResult::Incomplete: + case TemplateDeductionResult::IncompletePack: + case TemplateDeductionResult::Inconsistent: + case TemplateDeductionResult::Underqualified: + case TemplateDeductionResult::DeducedMismatch: + case TemplateDeductionResult::DeducedMismatchNested: + case TemplateDeductionResult::TooManyArguments: + case TemplateDeductionResult::TooFewArguments: + case TemplateDeductionResult::InvalidExplicitArguments: + case TemplateDeductionResult::NonDependentConversionFailure: + case TemplateDeductionResult::ConstraintsNotSatisfied: + case TemplateDeductionResult::MiscellaneousDeductionFailure: + case TemplateDeductionResult::CUDATargetMismatch: + llvm_unreachable("Unexpected Result"); + } + llvm_unreachable("Unexpected TDK"); } namespace { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 839c4e8a28220..3dc5696bd3821 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -575,6 +575,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case LambdaExpressionSubstitution: case BuildingDeductionGuides: case TypeAliasTemplateInstantiation: + case PartialOrderingTTP: return false; // This function should never be called when Kind's value is Memoization. @@ -805,6 +806,11 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( SemaRef, CodeSynthesisContext::BuildingDeductionGuides, PointOfInstantiation, InstantiationRange, Entity) {} +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation ArgLoc, PartialOrderingTTP, + TemplateDecl *PArg, SourceRange InstantiationRange) + : InstantiatingTemplate(SemaRef, CodeSynthesisContext::PartialOrderingTTP, + ArgLoc, InstantiationRange, PArg) {} void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) { Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext; @@ -1243,6 +1249,14 @@ void Sema::PrintInstantiationStack() { << cast(Active->Entity) << Active->InstantiationRange; break; + case CodeSynthesisContext::PartialOrderingTTP: + Diags.Report(Active->PointOfInstantiation, + diag::note_template_arg_template_params_mismatch); + if (SourceLocation ParamLoc = Active->Entity->getLocation(); + ParamLoc.isValid()) + Diags.Report(ParamLoc, diag::note_template_prev_declaration) + << /*isTemplateTemplateParam=*/true << Active->InstantiationRange; + break; } } } @@ -1285,6 +1299,7 @@ std::optional Sema::isSFINAEContext() const { case CodeSynthesisContext::PriorTemplateArgumentSubstitution: case CodeSynthesisContext::DefaultTemplateArgumentChecking: case CodeSynthesisContext::RewritingOperatorAsSpaceship: + case CodeSynthesisContext::PartialOrderingTTP: // A default template argument instantiation and substitution into // template parameters with arguments for prior parameters may or may // not be a SFINAE context; look further up the stack. @@ -4039,11 +4054,11 @@ bool Sema::usesPartialOrExplicitSpecialization( /// Get the instantiation pattern to use to instantiate the definition of a /// given ClassTemplateSpecializationDecl (either the pattern of the primary /// template or of a partial specialization). -static ActionResult -getPatternForClassTemplateSpecialization( +static ActionResult getPatternForClassTemplateSpecialization( Sema &S, SourceLocation PointOfInstantiation, ClassTemplateSpecializationDecl *ClassTemplateSpec, - TemplateSpecializationKind TSK) { + TemplateSpecializationKind TSK, + bool PrimaryHasMatchedPackOnParmToNonPackOnArg) { Sema::InstantiatingTemplate Inst(S, PointOfInstantiation, ClassTemplateSpec); if (Inst.isInvalid()) return {/*Invalid=*/true}; @@ -4066,7 +4081,7 @@ getPatternForClassTemplateSpecialization( // specialization with the template argument lists of the partial // specializations. typedef PartialSpecMatchResult MatchResult; - SmallVector Matched; + SmallVector Matched, ExtraMatched; SmallVector PartialSpecs; Template->getPartialSpecializations(PartialSpecs); TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation); @@ -4096,11 +4111,13 @@ getPatternForClassTemplateSpecialization( MakeDeductionFailureInfo(S.Context, Result, Info)); (void)Result; } else { - Matched.push_back(PartialSpecMatchResult()); - Matched.back().Partial = Partial; - Matched.back().Args = Info.takeCanonical(); + auto &List = + Info.hasMatchedPackOnParmToNonPackOnArg() ? ExtraMatched : Matched; + List.push_back(MatchResult{Partial, Info.takeCanonical()}); } } + if (Matched.empty() && PrimaryHasMatchedPackOnParmToNonPackOnArg) + Matched = std::move(ExtraMatched); // If we're dealing with a member template where the template parameters // have been instantiated, this provides the original template parameters @@ -4203,7 +4220,8 @@ getPatternForClassTemplateSpecialization( bool Sema::InstantiateClassTemplateSpecialization( SourceLocation PointOfInstantiation, ClassTemplateSpecializationDecl *ClassTemplateSpec, - TemplateSpecializationKind TSK, bool Complain) { + TemplateSpecializationKind TSK, bool Complain, + bool PrimaryHasMatchedPackOnParmToNonPackOnArg) { // Perform the actual instantiation on the canonical declaration. ClassTemplateSpec = cast( ClassTemplateSpec->getCanonicalDecl()); @@ -4211,8 +4229,9 @@ bool Sema::InstantiateClassTemplateSpecialization( return true; ActionResult Pattern = - getPatternForClassTemplateSpecialization(*this, PointOfInstantiation, - ClassTemplateSpec, TSK); + getPatternForClassTemplateSpecialization( + *this, PointOfInstantiation, ClassTemplateSpec, TSK, + PrimaryHasMatchedPackOnParmToNonPackOnArg); if (!Pattern.isUsable()) return Pattern.isInvalid(); diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp index 19793fe826372..54fcfccad6f52 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp @@ -2,13 +2,13 @@ template struct eval; // expected-note 3{{template is declared here}} -template