Author: Yuanfang Chen Date: 2022-10-18T11:58:57-07:00 New Revision: 340eac01f7dad6c24cee35dd35f2484098dd6b1a
URL: https://github.com/llvm/llvm-project/commit/340eac01f7dad6c24cee35dd35f2484098dd6b1a DIFF: https://github.com/llvm/llvm-project/commit/340eac01f7dad6c24cee35dd35f2484098dd6b1a.diff LOG: [C++20] Implement P2113R0: Changes to the Partial Ordering of Constrained Functions This implementation matches GCC behavior in that [[ https://eel.is/c++draft/temp.func.order#6.2.1 | temp.func.order p6.2.1 ]] is not implemented [1]. I reached out to the GCC author to confirm that some changes elsewhere to overload resolution are probably needed, but no solution has been developed sufficiently [3]. Most of the wordings are implemented straightforwardly. However, for [[ https://eel.is/c++draft/temp.func.order#6.2.2 | temp.func.order p6.2.2 ]] "... or if the function parameters that positionally correspond between the two templates are not of the same type", the "same type" is not very clear ([2] is a bug related to this). Here is a quick example ``` template <C T, C U> int f(T, U); template <typename T, C U> int f(U, T); int x = f(0, 0); ``` Is the `U` and `T` from different `f`s the "same type"? The answer is NO even though both `U` and `T` are deduced to be `int` in this case. The reason is that `U` and `T` are dependent types, according to [[ https://eel.is/c++draft/temp.over.link#3 | temp.over.link p3 ]], they can not be the "same type". To check if two function parameters are the "same type": * For //function template//: compare the function parameter canonical types and return type between two function templates. * For //class template/partial specialization//: by [[ https://eel.is/c++draft/temp.spec.partial.order#1.2 | temp.spec.partial.order p1.2 ]], compare the injected template arguments between two templates using hashing(TemplateArgument::Profile) is enough. [1] https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=57b4daf8dc4ed7b669cc70638866ddb00f5b7746 [2] https://github.com/llvm/llvm-project/issues/49308 [3] https://lists.isocpp.org/core/2020/06/index.php#msg9392 Fixes https://github.com/llvm/llvm-project/issues/54039 Fixes https://github.com/llvm/llvm-project/issues/49308 (PR49964) Reviewed By: royjacobson, #clang-language-wg, mizvekov Differential Revision: https://reviews.llvm.org/D128750 Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/AST/DeclTemplate.h clang/include/clang/Sema/Sema.h clang/lib/AST/DeclTemplate.cpp clang/lib/Sema/SemaConcept.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp clang/www/cxx_status.html Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 9fe63dd5de840..cb704266df407 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -534,6 +534,9 @@ C++20 Feature Support which removes the requirement for the ``typename`` keyword in certain contexts. - Implemented The Equality Operator You Are Looking For (`P2468 <http://wg21.link/p2468r2>`_). +- Implemented `P2113R0: Proposed resolution for 2019 comment CA 112 <https://wg21.link/P2113R0>`_ + ([temp.func.order]p6.2.1 is not implemented, matching GCC). + C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 5cd1de4d24132..ae10744383559 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -847,6 +847,15 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// The first value in the array is the number of specializations/partial /// specializations that follow. uint32_t *LazySpecializations = nullptr; + + /// The set of "injected" template arguments used within this + /// template. + /// + /// This pointer refers to the template arguments (there are as + /// many template arguments as template parameaters) for the + /// template, and is allocated lazily, since most templates do not + /// require the use of this information. + TemplateArgument *InjectedArgs = nullptr; }; /// Pointer to the common data shared by all declarations of this @@ -954,6 +963,14 @@ class RedeclarableTemplateDecl : public TemplateDecl, getCommonPtr()->InstantiatedFromMember.setPointer(TD); } + /// Retrieve the "injected" template arguments that correspond to the + /// template parameters of this template. + /// + /// Although the C++ standard has no notion of the "injected" template + /// arguments for a template, the notion is convenient when + /// we need to perform substitutions inside the definition of a template. + ArrayRef<TemplateArgument> getInjectedTemplateArgs(); + using redecl_range = redeclarable_base::redecl_range; using redecl_iterator = redeclarable_base::redecl_iterator; @@ -998,15 +1015,6 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { /// template, including explicit specializations and instantiations. llvm::FoldingSetVector<FunctionTemplateSpecializationInfo> Specializations; - /// The set of "injected" template arguments used within this - /// function template. - /// - /// This pointer refers to the template arguments (there are as - /// many template arguments as template parameaters) for the function - /// template, and is allocated lazily, since most function templates do not - /// require the use of this information. - TemplateArgument *InjectedArgs = nullptr; - Common() = default; }; @@ -1106,15 +1114,6 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { return makeSpecIterator(getSpecializations(), true); } - /// Retrieve the "injected" template arguments that correspond to the - /// template parameters of this function template. - /// - /// Although the C++ standard has no notion of the "injected" template - /// arguments for a function template, the notion is convenient when - /// we need to perform substitutions inside the definition of a function - /// template. - ArrayRef<TemplateArgument> getInjectedTemplateArgs(); - /// Return whether this function template is an abbreviated function template, /// e.g. `void foo(auto x)` or `template<typename T> void foo(auto x)` bool isAbbreviated() const { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6b422c3897c38..6091a0be01089 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8329,14 +8329,17 @@ class Sema final { const NamedDecl *NewInstFrom, TemplateParameterList *New, const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain, TemplateParameterListEqualKind Kind, - SourceLocation TemplateArgLoc = SourceLocation()); + SourceLocation TemplateArgLoc = SourceLocation(), + bool PartialOrdering = false); bool TemplateParameterListsAreEqual( TemplateParameterList *New, TemplateParameterList *Old, bool Complain, TemplateParameterListEqualKind Kind, - SourceLocation TemplateArgLoc = SourceLocation()) { + SourceLocation TemplateArgLoc = SourceLocation(), + bool PartialOrdering = false) { return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain, - Kind, TemplateArgLoc); + Kind, TemplateArgLoc, + PartialOrdering); } bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams); @@ -9021,8 +9024,7 @@ class Sema final { FunctionTemplateDecl *getMoreSpecializedTemplate( FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc, TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1, - unsigned NumCallArguments2, bool Reversed = false, - bool AllowOrderingByConstraints = true); + unsigned NumCallArguments2, bool Reversed = false); UnresolvedSetIterator getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd, TemplateSpecCandidateSet &FailedCandidates, diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index c419cb7006b5f..9a2932c0bcbd8 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -353,6 +353,22 @@ void RedeclarableTemplateDecl::addSpecializationImpl( SETraits::getDecl(Entry)); } +ArrayRef<TemplateArgument> RedeclarableTemplateDecl::getInjectedTemplateArgs() { + TemplateParameterList *Params = getTemplateParameters(); + auto *CommonPtr = getCommonPtr(); + if (!CommonPtr->InjectedArgs) { + auto &Context = getASTContext(); + SmallVector<TemplateArgument, 16> TemplateArgs; + Context.getInjectedTemplateArgs(Params, TemplateArgs); + CommonPtr->InjectedArgs = + new (Context) TemplateArgument[TemplateArgs.size()]; + std::copy(TemplateArgs.begin(), TemplateArgs.end(), + CommonPtr->InjectedArgs); + } + + return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size()); +} + //===----------------------------------------------------------------------===// // FunctionTemplateDecl Implementation //===----------------------------------------------------------------------===// @@ -403,22 +419,6 @@ void FunctionTemplateDecl::addSpecialization( InsertPos); } -ArrayRef<TemplateArgument> FunctionTemplateDecl::getInjectedTemplateArgs() { - TemplateParameterList *Params = getTemplateParameters(); - Common *CommonPtr = getCommonPtr(); - if (!CommonPtr->InjectedArgs) { - auto &Context = getASTContext(); - SmallVector<TemplateArgument, 16> TemplateArgs; - Context.getInjectedTemplateArgs(Params, TemplateArgs); - CommonPtr->InjectedArgs = - new (Context) TemplateArgument[TemplateArgs.size()]; - std::copy(TemplateArgs.begin(), TemplateArgs.end(), - CommonPtr->InjectedArgs); - } - - return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size()); -} - void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { using Base = RedeclarableTemplateDecl; diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index e1ebfb1e6f81b..5d8d7a41f821c 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1104,6 +1104,18 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) { // - The normal form of an expression (E) is the normal form of E. // [...] E = E->IgnoreParenImpCasts(); + + // C++2a [temp.param]p4: + // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). + // + // Using the pattern suffices because the partial ordering rules guarantee + // the template paramaters are equivalent. + if (auto *FoldE = dyn_cast<const CXXFoldExpr>(E)) { + assert(FoldE->isRightFold() && FoldE->getOperator() == BO_LAnd); + assert(E->IgnoreParenImpCasts() == E); + E = FoldE->getPattern(); + } + if (LogicalBinOp BO = E) { auto LHS = fromConstraintExpr(S, D, BO.getLHS()); if (!LHS) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 89df99fb6320f..721056f1f9dab 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -9776,19 +9776,13 @@ static bool haveSameParameterTypes(ASTContext &Context, const FunctionDecl *F1, /// We're allowed to use constraints partial ordering only if the candidates /// have the same parameter types: -/// [temp.func.order]p6.2.2 [...] 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. /// [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<FunctionProtoType>(Cand1.Function->getFunctionType()); auto *PT2 = cast<FunctionProtoType>(Cand2.Function->getFunctionType()); if (PT1->getNumParams() == PT2->getNumParams() && @@ -10031,15 +10025,14 @@ bool clang::isBetterOverloadCandidate( isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion : TPOC_Call, Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments, - Cand1.isReversed() ^ Cand2.isReversed(), - canCompareFunctionConstraints(S, Cand1, Cand2))) + Cand1.isReversed() ^ Cand2.isReversed())) 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 index 44b55427bf132..551e0c3cf3b07 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -7652,10 +7652,10 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, 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<const Expr *, 3> ParamsAC, TemplateAC; Params->getAssociatedConstraints(ParamsAC); // C++2a[temp.arg.template]p3 @@ -7864,7 +7864,8 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, static bool MatchTemplateParameterKind( Sema &S, NamedDecl *New, const NamedDecl *NewInstFrom, NamedDecl *Old, const NamedDecl *OldInstFrom, bool Complain, - Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { + Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc, + bool PartialOrdering) { // Check the actual kind (type, non-type, template). if (Old->getKind() != New->getKind()) { if (Complain) { @@ -7952,11 +7953,11 @@ static bool MatchTemplateParameterKind( (Kind == Sema::TPL_TemplateMatch ? Sema::TPL_TemplateTemplateParmMatch : Kind), - TemplateArgLoc)) + TemplateArgLoc, PartialOrdering)) return false; } - if (Kind != Sema::TPL_TemplateTemplateArgumentMatch && + if (!PartialOrdering && Kind != Sema::TPL_TemplateTemplateArgumentMatch && !isa<TemplateTemplateParmDecl>(Old)) { const Expr *NewC = nullptr, *OldC = nullptr; @@ -8049,7 +8050,8 @@ void DiagnoseTemplateParameterListArityMismatch(Sema &S, bool Sema::TemplateParameterListsAreEqual( const NamedDecl *NewInstFrom, TemplateParameterList *New, const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain, - TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { + TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc, + bool PartialOrdering) { if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) { if (Complain) DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind, @@ -8081,7 +8083,7 @@ bool Sema::TemplateParameterListsAreEqual( if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm, OldInstFrom, Complain, Kind, - TemplateArgLoc)) + TemplateArgLoc, PartialOrdering)) return false; ++NewParm; @@ -8098,7 +8100,7 @@ bool Sema::TemplateParameterListsAreEqual( for (; NewParm != NewParmEnd; ++NewParm) { if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm, OldInstFrom, Complain, Kind, - TemplateArgLoc)) + TemplateArgLoc, PartialOrdering)) return false; } } @@ -8112,7 +8114,7 @@ bool Sema::TemplateParameterListsAreEqual( 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 index 822ec1828dce4..00b1cf1224e2b 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5172,43 +5172,26 @@ static bool isAtLeastAsSpecializedAs(Sema &S, /// candidate with a reversed parameter order. In this case, the corresponding /// P/A pairs between FT1 and FT2 are reversed. /// -/// \param AllowOrderingByConstraints If \c is false, don't check whether one -/// of the templates is more constrained than the other. Default is true. -/// /// \returns the more specialized function template. If neither /// template is more specialized, returns NULL. FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc, TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1, - unsigned NumCallArguments2, bool Reversed, - bool AllowOrderingByConstraints) { - - auto JudgeByConstraints = [&]() -> FunctionTemplateDecl * { - if (!AllowOrderingByConstraints) - return nullptr; - llvm::SmallVector<const Expr *, 3> 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; - }; + unsigned NumCallArguments2, bool Reversed) { bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, NumCallArguments1, Reversed); bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, 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(); + return nullptr; // C++ [temp.deduct.partial]p11: // ... and if G has a trailing function parameter pack for which F does not @@ -5260,7 +5243,61 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( } } - return JudgeByConstraints(); + if (!Context.getLangOpts().CPlusPlus20 || isa<CXXDeductionGuideDecl>(FD1) || + isa<CXXDeductionGuideDecl>(FD2)) + return nullptr; + + // Match GCC on not implementing [temp.func.order]p6.2.1. + + // C++20 [temp.func.order]p6: + // If deduction against the other template succeeds for both transformed + // templates, constraints can be considered as follows: + + // C++20 [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 diff er in length, neither template is more specialized + // than the other. + TemplateParameterList *TPL1 = FT1->getTemplateParameters(); + TemplateParameterList *TPL2 = FT2->getTemplateParameters(); + if (TPL1->size() != TPL2->size() || NumParams1 != NumParams2) + return nullptr; + + // C++20 [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 (!TemplateParameterListsAreEqual( + TPL1, TPL2, false, Sema::TPL_TemplateMatch, SourceLocation(), true)) + return nullptr; + + for (unsigned i = 0; i < NumParams1; ++i) + if (!Context.hasSameType(FD1->getParamDecl(i)->getType(), + FD2->getParamDecl(i)->getType())) + return nullptr; + + // C++20 [temp.func.order]p6.3: + // Otherwise, if the context in which the partial ordering is done is + // that of a call to a conversion function and the return types of the + // templates are not the same, then neither template is more specialized + // than the other. + if (TPOC == TPOC_Conversion && + !Context.hasSameType(FD1->getReturnType(), FD2->getReturnType())) + return nullptr; + + llvm::SmallVector<const Expr *, 3> 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; } /// Determine if the two templates are equivalent. @@ -5438,7 +5475,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, } namespace { -// A dummy pass to return nullptr instead of P2 when performing "more +// A dummy class to return nullptr instead of P2 when performing "more // specialized than primary" check. struct GetP2 { template <typename T1, typename T2, @@ -5452,6 +5489,52 @@ struct GetP2 { return nullptr; } }; + +// The assumption is that two template argument lists have the same size. +struct TemplateArgumentListAreEqual { + ASTContext &Ctx; + TemplateArgumentListAreEqual(ASTContext &Ctx) : Ctx(Ctx) {} + + template <typename T1, typename T2, + std::enable_if_t<std::is_same<T1, T2>::value, bool> = true> + bool operator()(T1 *PS1, T2 *PS2) { + ArrayRef<TemplateArgument> Args1 = PS1->getTemplateArgs().asArray(), + Args2 = PS2->getTemplateArgs().asArray(); + + for (unsigned I = 0, E = Args1.size(); I < E; ++I) { + // We use profile, instead of structural comparison of the arguments, + // because canonicalization can't do the right thing for dependent + // expressions. + llvm::FoldingSetNodeID IDA, IDB; + Args1[I].Profile(IDA, Ctx); + Args2[I].Profile(IDB, Ctx); + if (IDA != IDB) + return false; + } + return true; + } + + template <typename T1, typename T2, + std::enable_if_t<!std::is_same<T1, T2>::value, bool> = true> + bool operator()(T1 *Spec, T2 *Primary) { + ArrayRef<TemplateArgument> Args1 = Spec->getTemplateArgs().asArray(), + Args2 = Primary->getInjectedTemplateArgs(); + + for (unsigned I = 0, E = Args1.size(); I < E; ++I) { + // We use profile, instead of structural comparison of the arguments, + // because canonicalization can't do the right thing for dependent + // expressions. + llvm::FoldingSetNodeID IDA, IDB; + Args1[I].Profile(IDA, Ctx); + // Unlike the specialization arguments, the injected arguments are not + // always canonical. + Ctx.getCanonicalTemplateArgument(Args2[I]).Profile(IDB, Ctx); + if (IDA != IDB) + return false; + } + return true; + } +}; } // namespace /// Returns the more specialized template specialization between T1/P1 and @@ -5491,52 +5574,81 @@ getMoreSpecialized(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P1, if (IsMoreSpecialThanPrimaryCheck && !Better2) return P1; + // 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 ? P1 : GetP2()(P1, P2); + if (!Better1 && !Better2) return nullptr; - if (Better1 && Better2) { - // This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that - // there is no wording or even resolution for this issue. - bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver15; - if (!ClangABICompat15) { - auto *TST1 = cast<TemplateSpecializationType>(T1); - auto *TST2 = cast<TemplateSpecializationType>(T2); - const TemplateArgument &TA1 = TST1->template_arguments().back(); - if (TA1.getKind() == TemplateArgument::Pack) { - assert(TST1->getNumArgs() == TST2->getNumArgs()); - const TemplateArgument &TA2 = TST2->template_arguments().back(); - assert(TA2.getKind() == TemplateArgument::Pack); - unsigned PackSize1 = TA1.pack_size(); - unsigned PackSize2 = TA2.pack_size(); - bool IsPackExpansion1 = - PackSize1 && TA1.pack_elements().back().isPackExpansion(); - bool IsPackExpansion2 = - PackSize2 && TA2.pack_elements().back().isPackExpansion(); - if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) { - if (PackSize1 > PackSize2 && IsPackExpansion1) - return GetP2()(P1, P2); - if (PackSize1 < PackSize2 && IsPackExpansion2) - return P1; - } + // This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that + // there is no wording or even resolution for this issue. + bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver15; + if (!ClangABICompat15) { + auto *TST1 = cast<TemplateSpecializationType>(T1); + auto *TST2 = cast<TemplateSpecializationType>(T2); + const TemplateArgument &TA1 = TST1->template_arguments().back(); + if (TA1.getKind() == TemplateArgument::Pack) { + assert(TST1->getNumArgs() == TST2->getNumArgs()); + const TemplateArgument &TA2 = TST2->template_arguments().back(); + assert(TA2.getKind() == TemplateArgument::Pack); + unsigned PackSize1 = TA1.pack_size(); + unsigned PackSize2 = TA2.pack_size(); + bool IsPackExpansion1 = + PackSize1 && TA1.pack_elements().back().isPackExpansion(); + bool IsPackExpansion2 = + PackSize2 && TA2.pack_elements().back().isPackExpansion(); + if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) { + if (PackSize1 > PackSize2 && IsPackExpansion1) + return GetP2()(P1, P2); + if (PackSize1 < PackSize2 && IsPackExpansion2) + return P1; } } - - llvm::SmallVector<const Expr *, 3> AC1, AC2; - P1->getAssociatedConstraints(AC1); - P2->getAssociatedConstraints(AC2); - bool AtLeastAsConstrained1, AtLeastAsConstrained2; - if (S.IsAtLeastAsConstrained(P1, AC1, P2, AC2, AtLeastAsConstrained1) || - (IsMoreSpecialThanPrimaryCheck && !AtLeastAsConstrained1)) - return nullptr; - if (S.IsAtLeastAsConstrained(P2, AC2, P1, AC1, AtLeastAsConstrained2)) - return nullptr; - if (AtLeastAsConstrained1 == AtLeastAsConstrained2) - return nullptr; - return AtLeastAsConstrained1 ? P1 : GetP2()(P1, P2); } - return Better1 ? P1 : GetP2()(P1, P2); + if (!S.Context.getLangOpts().CPlusPlus20) + return nullptr; + + // Match GCC on not implementing [temp.func.order]p6.2.1. + + // C++20 [temp.func.order]p6: + // If deduction against the other template succeeds for both transformed + // templates, constraints can be considered as follows: + + TemplateParameterList *TPL1 = P1->getTemplateParameters(); + TemplateParameterList *TPL2 = P2->getTemplateParameters(); + if (TPL1->size() != TPL2->size()) + return nullptr; + + // C++20 [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 (!S.TemplateParameterListsAreEqual( + TPL1, TPL2, false, Sema::TPL_TemplateMatch, SourceLocation(), true)) + return nullptr; + + if (!TemplateArgumentListAreEqual(S.getASTContext())(P1, P2)) + return nullptr; + + llvm::SmallVector<const Expr *, 3> AC1, AC2; + P1->getAssociatedConstraints(AC1); + P2->getAssociatedConstraints(AC2); + bool AtLeastAsConstrained1, AtLeastAsConstrained2; + if (S.IsAtLeastAsConstrained(P1, AC1, P2, AC2, AtLeastAsConstrained1) || + (IsMoreSpecialThanPrimaryCheck && !AtLeastAsConstrained1)) + return nullptr; + if (S.IsAtLeastAsConstrained(P2, AC2, P1, AC1, AtLeastAsConstrained2)) + return nullptr; + if (AtLeastAsConstrained1 == AtLeastAsConstrained2) + return nullptr; + return AtLeastAsConstrained1 ? P1 : GetP2()(P1, P2); } /// Returns the more specialized class template partial specialization @@ -5596,17 +5708,11 @@ Sema::getMoreSpecializedPartialSpecialization( bool Sema::isMoreSpecializedThanPrimary( VarTemplatePartialSpecializationDecl *Spec, TemplateDeductionInfo &Info) { - TemplateDecl *Primary = Spec->getSpecializedTemplate(); - // FIXME: Cache the injected template arguments rather than recomputing - // them for each partial specialization. - SmallVector<TemplateArgument, 8> PrimaryArgs; - Context.getInjectedTemplateArgs(Primary->getTemplateParameters(), - PrimaryArgs); - + VarTemplateDecl *Primary = Spec->getSpecializedTemplate(); TemplateName CanonTemplate = Context.getCanonicalTemplateName(TemplateName(Primary)); QualType PrimaryT = Context.getTemplateSpecializationType( - CanonTemplate, PrimaryArgs); + CanonTemplate, Primary->getInjectedTemplateArgs()); QualType PartialT = Context.getTemplateSpecializationType( CanonTemplate, Spec->getTemplateArgs().asArray()); diff --git a/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp b/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp index 047d3dc13cc31..dae1ba760cc20 100644 --- a/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp +++ b/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp @@ -98,26 +98,31 @@ namespace non_template static_assert(is_same_v<decltype(bar<int>()), void>); // expected-error {{call to 'bar' is ambiguous}} template<typename T> - constexpr int goo(int a) requires AtLeast2<int> && true { + constexpr int goo(int a) requires AtLeast2<int> && true { // expected-note {{candidate function}} return 1; } template<typename T> - constexpr int goo(const int b) requires AtLeast2<int> { + constexpr int goo(const int b) requires AtLeast2<int> { // expected-note {{candidate function}} return 2; } - // Only trailing requires clauses of redeclarations are compared for overload resolution. + // [temp.func.order] p5 + // Since, in a call context, such type deduction considers only parameters + // for which there are explicit call arguments, some parameters are ignored + // (namely, function parameter packs, parameters with default arguments, and + // ellipsis parameters). template<typename T> - constexpr int doo(int a, ...) requires AtLeast2<int> && true { // expected-note {{candidate function}} + constexpr int doo(int a, ...) requires AtLeast2<int> && true { return 1; } template<typename T> - constexpr int doo(int b) requires AtLeast2<int> { // expected-note {{candidate function}} + constexpr int doo(int b) requires AtLeast2<int> { return 2; } - static_assert(goo<int>(1) == 1); - static_assert(doo<int>(2) == 1); // expected-error {{call to 'doo' is ambiguous}} + // By temp.func.order-6.2.2, this is ambiguous because parameter a and b have diff erent types. + static_assert(goo<int>(1) == 1); // expected-error {{call to 'goo' is ambiguous}} + static_assert(doo<int>(2) == 1); } 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 index d2c7ec91c3fc8..bdd1c376243bb 100644 --- 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 @@ -1,10 +1,18 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s -struct A; -struct B; - template <typename> constexpr bool True = true; template <typename T> concept C = True<T>; +template <typename T> concept D = C<T> && sizeof(T) > 2; +template <typename T> concept E = D<T> && alignof(T) > 1; + +struct A {}; +template <typename, auto, int, A, typename...> struct S {}; +template <typename, auto, int, A, auto...> struct S2 {}; +template <typename T, typename U> struct X {}; + +namespace p6 { + +struct B; void f(C auto &, auto &) = delete; template <C Q> void f(Q &, C auto &); @@ -13,14 +21,125 @@ void g(struct A *ap, struct B *bp) { f(*ap, *bp); } -template <typename T, typename U> struct X {}; - +#if 0 +// FIXME: [temp.func.order]p6.2.1 is not implemented, matching GCC. template <typename T, C U, typename V> bool operator==(X<T, U>, V) = delete; template <C T, C U, C V> bool operator==(T, X<U, V>); bool h() { return X<void *, int>{} == 0; } +#endif + +template<C T, C auto M, int W, A S, + template<typename, auto, int, A, typename...> class U, + typename... Z> +void foo(T, U<T, M, W, S, Z...>) = delete; +template<C T, D auto M, int W, A S, + template<typename, auto, int, A, typename...> class U, + typename... Z> +void foo(T, U<T, M, W, S, Z...>) = delete; +template<C T, E auto M, int W, A S, + template<typename, auto, int, A, typename...> class U, + typename... Z> +void foo(T, U<T, M, W, S, Z...>); + +// check auto template parameter pack. +template<C T, auto M, int W, A S, + template<typename, auto, int, A, auto...> class U, + C auto... Z> +void foo2(T, U<T, M, W, S, Z...>) = delete; +template<C T, auto M, int W, A S, + template<typename, auto, int, A, auto...> class U, + D auto... Z> +void foo2(T, U<T, M, W, S, Z...>) = delete; +template<C T, auto M, int W, A S, + template<typename, auto, int, A, auto...> class U, + E auto... Z> +void foo2(T, U<T, M, W, S, Z...>); + +void bar(S<int, 1, 1, A{}, int> s, S2<int, 1, 1, A{}, 0, 0u> s2) { + foo(0, s); + foo2(0, s2); +} + +template<C auto... T> void bar2(); +template<D auto... T> void bar2() = delete; + +} // namespace p6 + +namespace TestConversionFunction { +struct Y { + template<C T, typename U> operator X<T, U>(); // expected-note {{candidate function [with T = int, U = int]}} + template<typename T, typename U> operator X<U, T>(); // expected-note {{candidate function [with T = int, U = int]}} +}; + +X<int,int> f() { + return Y{}; // expected-error {{conversion from 'Y' to 'X<int, int>' is ambiguous}} +} +} + +namespace ClassPartialSpecPartialOrdering { +template<D T> struct Y { Y()=delete; }; // expected-note {{template is declared here}} +template<C T> struct Y<T> {}; // expected-error {{class template partial specialization is not more specialized than the primary template}} + +template<C T, int I> struct Y1 { Y1()=delete; }; +template<D T> struct Y1<T, 2> { Y1()=delete; }; +template<E T> struct Y1<T, 1+1> {}; + +template<class T, int I, int U> struct Y2 {}; +template<class T, int I> struct Y2<T*, I, I+2> {}; // expected-note {{partial specialization matches}} +template<C T, int I> struct Y2<T*, I, I+1+1> {}; // expected-note {{partial specialization matches}} + +template<C T, C auto I, int W, A S, template<typename, auto, int, A, typename...> class U, typename... Z> +struct Y3 { Y3()=delete; }; +template<C T, D auto I, int W, A S, template<typename, auto, int, A, typename...> class U, typename... Z> +struct Y3<T, I, W, S, U, Z...> { Y3()=delete; }; +template<C T, E auto I, int W, A S, template<typename, auto, int, A, typename...> class U, typename... Z> +struct Y3<T, I, W, S, U, Z...> {}; + +void f() { + Y1<int, 2> a; + Y2<char*, 1, 3> b; // expected-error {{ambiguous partial specializations}} + Y3<int, 1, 1, A{}, S, int> c; +} + +template<C T, C V> struct Y4; // expected-note {{template is declared here}} +template<D T, C V> struct Y4<V, T>; // expected-error {{class template partial specialization is not more specialized than the primary template}} + +template<C auto T> struct W1; +template<D auto T> struct W1<T> {}; + +template<C auto... T> struct W2; +template<D auto... T> struct W2<T...> {}; + +template<class T, class U> +concept C1 = C<T> && C<U>; +template<class T, class U> +concept D1 = D<T> && C<U>; + +template<C1<A> auto T> struct W3; +template<D1<A> auto T> struct W3<T> {}; + +template<C1<A> auto... T> struct W4; +template<D1<A> auto... T> struct W4<T...> {}; + +// FIXME: enable once Clang support non-trivial auto on NTTP. +// template<C auto* T> struct W5; +// template<D auto* T> struct W5<T> {}; + +// FIXME: enable once Clang support non-trivial auto on NTTP. +// template<C auto& T> struct W6; +// template<D auto& T> struct W6<T> {}; + +struct W1<0> w1; +struct W2<0> w2; +struct W3<0> w3; +struct W4<0> w4; +// FIXME: enable once Clang support non-trivial auto on NTTP. +// struct W5<(int*)nullptr> w5; +// struct W6<w5> w6; +} namespace PR53640 { diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 632590a131a8b..cbea6bb5e1e26 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -963,7 +963,7 @@ <h2 id="cxx20">C++20 implementation status</h2> </tr> <tr> <td><a href="https://wg21.link/p2113r0">P2113R0</a></td> - <td rowspan="1" class="none" align="center">No</td> + <td rowspan="1" class="unreleased" align="center">Clang 16</td> </tr> <!-- Albuquerque papers --> <tr> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits