diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b3216e25ac75e..caca083409aa2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -204,7 +204,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. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2ba3a969ccc8d..d73b7dc2ab62a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11637,7 +11637,8 @@ class Sema final : public SemaBase { SourceLocation RAngleLoc, unsigned ArgumentPackIndex, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, - CheckTemplateArgumentKind CTAK); + CheckTemplateArgumentKind CTAK, + bool *MatchedPackOnParmToNonPackOnArg); /// Check that the given template arguments can be provided to /// the given template, converting the arguments along the way. @@ -11684,7 +11685,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, @@ -11718,7 +11720,8 @@ 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 IsDeduced, + bool *MatchedPackOnParmToNonPackOnArg); void NoteTemplateLocation(const NamedDecl &Decl, std::optional ParamRange = {}); @@ -12419,7 +12422,7 @@ class Sema final : public SemaBase { bool isTemplateTemplateParameterAtLeastAsSpecializedAs( TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg, const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, - bool IsDeduced); + bool IsDeduced, bool *MatchedPackOnParmToNonPackOnArg); /// Mark which template parameters are used in a given expression. /// @@ -13418,7 +13421,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/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index f3f62474d0644..31422c213ac24 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3666,7 +3666,8 @@ 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, + /*MatchedPackOnParmToNonPackOnArg=*/nullptr) || Trap.hasErrorOccurred()) IsTemplate = false; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 226c1172a059d..4f13669c2490c 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5179,7 +5179,7 @@ bool Sema::CheckTemplateArgument( unsigned ArgumentPackIndex, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, - CheckTemplateArgumentKind CTAK) { + CheckTemplateArgumentKind CTAK, bool *MatchedPackOnParmToNonPackOnArg) { // Check template type parameters. if (TemplateTypeParmDecl *TTP = dyn_cast(Param)) return CheckTemplateTypeArgument(TTP, Arg, SugaredConverted, @@ -5395,7 +5395,8 @@ bool Sema::CheckTemplateArgument( case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: if (CheckTemplateTemplateArgument(TempParm, Params, Arg, - /*IsDeduced=*/CTAK != CTAK_Specified)) + /*IsDeduced=*/CTAK != CTAK_Specified, + MatchedPackOnParmToNonPackOnArg)) return true; SugaredConverted.push_back(Arg.getArgument()); @@ -5469,7 +5470,7 @@ bool Sema::CheckTemplateArgumentList( SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied, - bool PartialOrderingTTP) { + bool PartialOrderingTTP, bool *MatchedPackOnParmToNonPackOnArg) { if (ConstraintsNotSatisfied) *ConstraintsNotSatisfied = false; @@ -5545,10 +5546,10 @@ 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)) + if (CheckTemplateArgument( + *Param, NewArgs[ArgIdx], Template, TemplateLoc, RAngleLoc, + SugaredArgumentPack.size(), SugaredConverted, CanonicalConverted, + CTAK_Specified, MatchedPackOnParmToNonPackOnArg)) return true; CanonicalConverted.back().setIsDefaulted( @@ -5706,7 +5707,8 @@ bool Sema::CheckTemplateArgumentList( // Check the default template argument. if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc, RAngleLoc, 0, SugaredConverted, CanonicalConverted, - CTAK_Specified)) + CTAK_Specified, + /*MatchedPackOnParmToNonPackOnArg=*/nullptr)) return true; SugaredConverted.back().setIsDefaulted(true); @@ -7289,10 +7291,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 IsDeduced, + bool *MatchedPackOnParmToNonPackOnArg) { TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern(); auto [Template, DefaultArgs] = Name.getTemplateDeclAndDefaultArgs(); if (!Template) { @@ -7336,7 +7338,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, // A template-argument matches a template template-parameter P when P // is at least as specialized as the template-argument A. if (!isTemplateTemplateParameterAtLeastAsSpecializedAs( - Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) + Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced, + MatchedPackOnParmToNonPackOnArg)) return true; // P2113 // C++20[temp.func.order]p2 @@ -9754,11 +9757,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 @@ -9879,7 +9885,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 03ff8145e3b4a..48a39a90f72a8 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2767,8 +2767,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, for (; hasTemplateArgumentForDeduction(As, ArgIdx) && PackScope.hasNextElement(); ++ArgIdx) { - if (!FoldPackParameter && !As[ArgIdx].isPackExpansion()) - return TemplateDeductionResult::MiscellaneousDeductionFailure; + 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, Pattern, As[ArgIdx], Info, PartialOrdering, @@ -2962,15 +2966,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, + &MatchedPackOnParmToNonPackOnArg); + if (MatchedPackOnParmToNonPackOnArg) + Info.setMatchedPackOnParmToNonPackOnArg(); + return Res; }; if (Arg.getKind() == TemplateArgument::Pack) { @@ -3165,7 +3174,8 @@ 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)) { + 0, SugaredBuilder, CanonicalBuilder, Sema::CTAK_Specified, + /*MatchedPackOnParmToNonPackOnArg=*/nullptr)) { Info.Param = makeTemplateParameter( const_cast(TemplateParams->getParam(I))); // FIXME: These template arguments are temporary. Free them! @@ -3314,16 +3324,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) { @@ -6465,8 +6479,8 @@ bool Sema::isMoreSpecializedThanPrimary( bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( TemplateParameterList *P, TemplateDecl *PArg, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, - bool IsDeduced) { + const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, bool IsDeduced, + 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 @@ -6518,11 +6532,11 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // If the rewrite produces an invalid type, then P is not at least as // specialized as A. SmallVector CanonicalPArgs; - if (CheckTemplateArgumentList(AArg, ArgLoc, PArgList, DefaultArgs, false, - PArgs, CanonicalPArgs, - /*UpdateArgsWithConversions=*/true, - /*ConstraintsNotSatisfied=*/nullptr, - /*PartialOrderingTTP=*/true)) + if (CheckTemplateArgumentList( + AArg, ArgLoc, PArgList, DefaultArgs, false, PArgs, CanonicalPArgs, + /*UpdateArgsWithConversions=*/true, + /*ConstraintsNotSatisfied=*/nullptr, + /*PartialOrderingTTP=*/true, MatchedPackOnParmToNonPackOnArg)) return false; } @@ -6548,6 +6562,9 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( IsDeduced ? PackFold::ArgumentToParameter : PackFold::Both, /*HasDeducedAnyParam=*/nullptr)) { case clang::TemplateDeductionResult::Success: + if (MatchedPackOnParmToNonPackOnArg && + Info.hasMatchedPackOnParmToNonPackOnArg()) + *MatchedPackOnParmToNonPackOnArg = true; break; case TemplateDeductionResult::MiscellaneousDeductionFailure: diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 4ce47d8c1ee76..74252bd7513cd 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -4004,11 +4004,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}; @@ -4031,7 +4031,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); @@ -4048,11 +4048,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 @@ -4155,7 +4157,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()); @@ -4163,8 +4166,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/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp index b9e9e9f0c97f2..56091e84cf4e9 100644 --- a/clang/test/SemaTemplate/cwg2398.cpp +++ b/clang/test/SemaTemplate/cwg2398.cpp @@ -156,16 +156,14 @@ namespace ttp_defaults { namespace ttp_only { template