diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8101,12 +8101,14 @@ TPL_TemplateTemplateArgumentMatch }; - bool TemplateParameterListsAreEqual(TemplateParameterList *New, - TemplateParameterList *Old, - bool Complain, - TemplateParameterListEqualKind Kind, - SourceLocation TemplateArgLoc - = SourceLocation()); + bool TemplateParametersAreEquivalent(TemplateParameterList *New, + TemplateParameterList *Old); + + bool TemplateParameterListsAreEqual( + TemplateParameterList *New, TemplateParameterList *Old, bool Complain, + TemplateParameterListEqualKind Kind, + SourceLocation TemplateArgLoc = SourceLocation(), + bool PartialOrdering = false); bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams); diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h --- a/clang/include/clang/Sema/TemplateDeduction.h +++ b/clang/include/clang/Sema/TemplateDeduction.h @@ -232,6 +232,8 @@ /// \brief The constraint satisfaction details resulting from the associated /// constraints satisfaction tests. ConstraintSatisfaction AssociatedConstraintsSatisfaction; + + SmallVector DeduceOrder; }; } // namespace sema diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -9646,13 +9646,10 @@ /// [over.match.best]p2.6 /// F1 and F2 are non-template functions with the same parameter-type-lists, /// and F1 is more constrained than F2 [...] -static bool canCompareFunctionConstraints(Sema &S, +static bool sameFunctionParameterTypeLists(Sema &S, const OverloadCandidate &Cand1, const OverloadCandidate &Cand2) { - // FIXME: Per P2113R0 we also need to compare the template parameter lists - // when comparing template functions. - if (Cand1.Function && Cand2.Function && Cand1.Function->hasPrototype() && - Cand2.Function->hasPrototype()) { + if (Cand1.Function && Cand2.Function) { auto *PT1 = cast(Cand1.Function->getFunctionType()); auto *PT2 = cast(Cand2.Function->getFunctionType()); if (PT1->getNumParams() == PT2->getNumParams() && @@ -9896,14 +9893,14 @@ : TPOC_Call, Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments, Cand1.isReversed() ^ Cand2.isReversed(), - canCompareFunctionConstraints(S, Cand1, Cand2))) + sameFunctionParameterTypeLists(S, Cand1, Cand2))) return BetterTemplate == Cand1.Function->getPrimaryTemplate(); } // -— F1 and F2 are non-template functions with the same // parameter-type-lists, and F1 is more constrained than F2 [...], if (!Cand1IsSpecialization && !Cand2IsSpecialization && - canCompareFunctionConstraints(S, Cand1, Cand2)) { + sameFunctionParameterTypeLists(S, Cand1, Cand2)) { Expr *RC1 = Cand1.Function->getTrailingRequiresClause(); Expr *RC2 = Cand2.Function->getTrailingRequiresClause(); if (RC1 && RC2) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -38,6 +38,7 @@ #include "llvm/ADT/StringExtras.h" #include +#include using namespace clang; using namespace sema; @@ -7446,10 +7447,10 @@ if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template, Arg.getLocation())) { - // C++2a[temp.func.order]p2 + // P2113 + // C++20[temp.func.order]p2 // [...] If both deductions succeed, the partial ordering selects the - // more constrained template as described by the rules in - // [temp.constr.order]. + // more constrained template (if one exists) as determined below. SmallVector ParamsAC, TemplateAC; Params->getAssociatedConstraints(ParamsAC); // C++2a[temp.arg.template]p3 @@ -7653,10 +7654,10 @@ } /// Match two template parameters within template parameter lists. -static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old, - bool Complain, - Sema::TemplateParameterListEqualKind Kind, - SourceLocation TemplateArgLoc) { +static bool MatchTemplateParameterKind( + Sema &S, NamedDecl *New, NamedDecl *Old, bool Complain, + Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc, + bool PartialOrdering) { // Check the actual kind (type, non-type, template). if (Old->getKind() != New->getKind()) { if (Complain) { @@ -7744,9 +7745,10 @@ (Kind == Sema::TPL_TemplateMatch ? Sema::TPL_TemplateTemplateParmMatch : Kind), - TemplateArgLoc)) + TemplateArgLoc, PartialOrdering)) return false; - } else if (Kind != Sema::TPL_TemplateTemplateArgumentMatch) { + } else if (!PartialOrdering && + Kind != Sema::TPL_TemplateTemplateArgumentMatch) { const Expr *NewC = nullptr, *OldC = nullptr; if (const auto *TC = cast(New)->getTypeConstraint()) NewC = TC->getImmediatelyDeclaredConstraint(); @@ -7803,6 +7805,17 @@ << SourceRange(Old->getTemplateLoc(), Old->getRAngleLoc()); } +bool Sema::TemplateParametersAreEquivalent(TemplateParameterList *New, + TemplateParameterList *Old) { + SmallVector IterIdxs(Old->size()); + std::iota(IterIdxs.begin(), IterIdxs.end(), 0); + return llvm::all_of(IterIdxs, [&](unsigned i) { + return MatchTemplateParameterKind(*this, New->getParam(i), Old->getParam(i), + false, Sema::TPL_TemplateMatch, + SourceLocation(), true); + }); +} + /// Determine whether the given template parameter lists are /// equivalent. /// @@ -7831,7 +7844,8 @@ TemplateParameterList *Old, bool Complain, TemplateParameterListEqualKind Kind, - SourceLocation TemplateArgLoc) { + SourceLocation TemplateArgLoc, + bool PartialOrdering) { if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) { if (Complain) DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind, @@ -7861,8 +7875,8 @@ return false; } - if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, - Kind, TemplateArgLoc)) + if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, Kind, + TemplateArgLoc, PartialOrdering)) return false; ++NewParm; @@ -7877,8 +7891,8 @@ // template parameter pack in P (ignoring whether those template // parameters are template parameter packs). for (; NewParm != NewParmEnd; ++NewParm) { - if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, - Kind, TemplateArgLoc)) + if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, Kind, + TemplateArgLoc, PartialOrdering)) return false; } } @@ -7892,7 +7906,7 @@ return false; } - if (Kind != TPL_TemplateTemplateArgumentMatch) { + if (!PartialOrdering && Kind != TPL_TemplateTemplateArgumentMatch) { const Expr *NewRC = New->getRequiresClause(); const Expr *OldRC = Old->getRequiresClause(); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -386,6 +386,7 @@ return Sema::TDK_Inconsistent; } + Info.DeduceOrder.push_back(NTTP->getIndex()); Deduced[NTTP->getIndex()] = Result; if (!S.getLangOpts().CPlusPlus17) return Sema::TDK_Success; @@ -512,6 +513,7 @@ return Sema::TDK_Inconsistent; } + Info.DeduceOrder.push_back(TempParam->getIndex()); Deduced[TempParam->getIndex()] = Result; return Sema::TDK_Success; } @@ -1517,6 +1519,7 @@ return Sema::TDK_Inconsistent; } + Info.DeduceOrder.push_back(Index); Deduced[Index] = Result; return Sema::TDK_Success; } @@ -4942,7 +4945,7 @@ /// Determine whether the function template \p FT1 is at least as /// specialized as \p FT2. static bool isAtLeastAsSpecializedAs(Sema &S, - SourceLocation Loc, + TemplateDeductionInfo &Info, FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, TemplatePartialOrderingContext TPOC, @@ -4963,7 +4966,6 @@ // C++0x [temp.deduct.partial]p3: // The types used to determine the ordering depend on the context in which // the partial ordering is done: - TemplateDeductionInfo Info(Loc); SmallVector Args2; switch (TPOC) { case TPOC_Call: { @@ -5111,16 +5113,7 @@ return false; ParmVarDecl *Last = Function->getParamDecl(NumParams - 1); - if (!Last->isParameterPack()) - return false; - - // Make sure that no previous parameter is a parameter pack. - while (--NumParams > 0) { - if (Function->getParamDecl(NumParams - 1)->isParameterPack()) - return false; - } - - return true; + return Last->isParameterPack(); } /// Returns the more specialized function template according @@ -5154,33 +5147,20 @@ unsigned NumCallArguments2, bool Reversed, bool AllowOrderingByConstraints) { - auto JudgeByConstraints = [&]() -> FunctionTemplateDecl * { - if (!AllowOrderingByConstraints) - return nullptr; - llvm::SmallVector AC1, AC2; - FT1->getAssociatedConstraints(AC1); - FT2->getAssociatedConstraints(AC2); - bool AtLeastAsConstrained1, AtLeastAsConstrained2; - if (IsAtLeastAsConstrained(FT1, AC1, FT2, AC2, AtLeastAsConstrained1)) - return nullptr; - if (IsAtLeastAsConstrained(FT2, AC2, FT1, AC1, AtLeastAsConstrained2)) - return nullptr; - if (AtLeastAsConstrained1 == AtLeastAsConstrained2) - return nullptr; - return AtLeastAsConstrained1 ? FT1 : FT2; - }; - - bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, + TemplateDeductionInfo Info1(Loc), Info2(Loc); + bool Better1 = isAtLeastAsSpecializedAs(*this, Info1, FT1, FT2, TPOC, NumCallArguments1, Reversed); - bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC, + bool Better2 = isAtLeastAsSpecializedAs(*this, Info2, FT2, FT1, TPOC, NumCallArguments2, Reversed); + // C++ [temp.deduct.partial]p10: + // F is more specialized than G if F is at least as specialized as G and G + // is not at least as specialized as F. if (Better1 != Better2) // We have a clear winner return Better1 ? FT1 : FT2; - if (!Better1 && !Better2) // Neither is better than the other - return JudgeByConstraints(); - + TemplateParameterList *TPL1 = FT1->getTemplateParameters(); + TemplateParameterList *TPL2 = FT2->getTemplateParameters(); const unsigned NumParams1 = FT1->getTemplatedDecl()->getNumParams(); const unsigned NumParams2 = FT2->getTemplatedDecl()->getNumParams(); @@ -5188,16 +5168,67 @@ // ... and if G has a trailing function parameter pack for which F does not // have a corresponding parameter, and if F does not have a trailing // function parameter pack, then F is more specialized than G. - bool Variadic1 = isVariadicFunctionTemplate(FT1); - bool Variadic2 = isVariadicFunctionTemplate(FT2); - if (Variadic1 != Variadic2) { - if (Variadic1 && NumParams1 > NumParams2) - return FT2; - if (Variadic2 && NumParams2 > NumParams1) - return FT1; + if (Better1 && Better2) { + bool Variadic1 = isVariadicFunctionTemplate(FT1); + bool Variadic2 = isVariadicFunctionTemplate(FT2); + if (Variadic1 != Variadic2) { + if (Variadic1 && NumParams1 > NumParams2) + return FT2; + if (Variadic2 && NumParams2 > NumParams1) + return FT1; + } } - return JudgeByConstraints(); + // C++ [temp.func.order]p6.1: + // If their template-parameter-lists (possibly including template-parameters + // invented for an abbreviated function template ([dcl.fct])) or function + // parameter lists differ in length, neither template is more specialized than + // the other. + if (TPL1->size() != TPL2->size() || NumParams1 != NumParams2) + return nullptr; + + if (Reversed) { + // C++ [temp.func.order]p6.2.1.1: + // If, for either template, some of the template parameters are not + // deducible from their function parameters, neither template is more + // specialized than the other. + llvm::SmallBitVector Deduced1, Deduced2; + MarkDeducedTemplateParameters(Context, FT1, Deduced1); + MarkDeducedTemplateParameters(Context, FT2, Deduced2); + if (!Deduced1.all() || !Deduced2.all()) + return nullptr; + } + + // C++ [temp.func.order]p6.2.1.2: + // If there is either no reordering or more than one reordering of the + // associated template-parameter-list such that ... + // + // C++ [temp.func.order]p6.2.2: + // Otherwise, if the corresponding template-parameters of the + // template-parameter-lists are not equivalent ([temp.over.link]) or if the + // function parameters that positionally correspond between the two + // templates are not of the same type, neither template is more specialized + // than the other. + if (!Reversed && !llvm::equal(Info1.DeduceOrder, Info2.DeduceOrder)) + return nullptr; + if (!TemplateParametersAreEquivalent(TPL1, TPL2)) + return nullptr; + + if (AllowOrderingByConstraints) { + llvm::SmallVector AC1, AC2; + FT1->getAssociatedConstraints(AC1); + FT2->getAssociatedConstraints(AC2); + bool AtLeastAsConstrained1, AtLeastAsConstrained2; + if (IsAtLeastAsConstrained(FT1, AC1, FT2, AC2, AtLeastAsConstrained1)) + return nullptr; + if (IsAtLeastAsConstrained(FT2, AC2, FT1, AC1, AtLeastAsConstrained2)) + return nullptr; + if (AtLeastAsConstrained1 == AtLeastAsConstrained2) + return nullptr; + return AtLeastAsConstrained1 ? FT1 : FT2; + } + + return nullptr; } /// Determine if the two templates are equivalent. diff --git a/clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p3.cpp b/clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p3.cpp --- a/clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p3.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p3.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s // expected-no-diagnostics namespace OrderWithStaticMember { @@ -37,3 +38,17 @@ void f(S s, V v) { s >> v; } } #endif + +#if __cplusplus >= 202002L +namespace CWG2445 { + template struct A { }; + + template + bool operator==(T, A); + + template + bool operator!=(A, U) = delete; + + bool f(A ax, A ay) { return ay != ax; } +} +#endif diff --git a/clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp b/clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp --- a/clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp @@ -15,12 +15,32 @@ template struct X {}; +namespace p6_2_1_1 { +template + bool operator==(X, V); +template bool operator==(T, X) = delete; + +bool h() { + // Prefer the first operator== since it is not a rewritten candidate. + return X{} == 0; +} +} + +namespace p6 { template bool operator==(X, V) = delete; template bool operator==(T, X); bool h() { return X{} == 0; } +} // namespace p6 + +namespace PR49964 { + template int f(T, U); // expected-note {{candidate function [with T = int, U = int]}} + template int f(U, T); // expected-note {{candidate function [with T = int, U = int]}} + + int x = f(0, 0); // expected-error {{call to 'f' is ambiguous}} +} namespace PR53640 {