diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -191,6 +191,8 @@ and `DR1734 `_. - Class member variables are now in scope when parsing a ``requires`` clause. Fixes `GH55216 `_. +- Implemented `P2113R0: Proposed resolution for 2019 comment CA 112 `_ + ([temp.func.order]p6.2.1 is not implemented, matching GCC). - Correctly set expression evaluation context as 'immediate function context' in consteval functions. diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -845,6 +845,15 @@ /// 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 @@ -952,6 +961,14 @@ 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 getInjectedTemplateArgs(); + using redecl_range = redeclarable_base::redecl_range; using redecl_iterator = redeclarable_base::redecl_iterator; @@ -996,15 +1013,6 @@ /// template, including explicit specializations and instantiations. llvm::FoldingSetVector 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; }; @@ -1104,15 +1112,6 @@ 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 getInjectedTemplateArgs(); - /// Return whether this function template is an abbreviated function template, /// e.g. `void foo(auto x)` or `template void foo(auto x)` bool isAbbreviated() const { 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 @@ -8208,12 +8208,11 @@ TPL_TemplateTemplateArgumentMatch }; - bool TemplateParameterListsAreEqual(TemplateParameterList *New, - TemplateParameterList *Old, - bool Complain, - TemplateParameterListEqualKind Kind, - SourceLocation TemplateArgLoc - = SourceLocation()); + bool TemplateParameterListsAreEqual( + TemplateParameterList *New, TemplateParameterList *Old, bool Complain, + TemplateParameterListEqualKind Kind, + SourceLocation TemplateArgLoc = SourceLocation(), + bool PartialOrdering = false); bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams); @@ -8904,8 +8903,7 @@ 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/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h --- a/clang/include/clang/Sema/SemaConcept.h +++ b/clang/include/clang/Sema/SemaConcept.h @@ -43,11 +43,33 @@ return false; for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) { + const TemplateArgument &ArgA = (*ParameterMapping)[I].getArgument(); + const TemplateArgument &ArgB = (*Other.ParameterMapping)[I].getArgument(); + if (ArgA.getKind() == TemplateArgument::Expression && + ArgB.getKind() == TemplateArgument::Expression && + ArgA.getAsExpr()->getType()->isUndeducedAutoType() && + ArgB.getAsExpr()->getType()->isUndeducedAutoType()) + continue; + + if (ArgA.getKind() == TemplateArgument::Type && + ArgB.getKind() == TemplateArgument::Type) + if (const auto *SubstA = + ArgA.getAsType()->getAs()) + if (const auto *SubstB = + ArgB.getAsType()->getAs()) { + QualType ReplacementA = SubstA->getReplacementType(); + QualType ReplacementB = SubstB->getReplacementType(); + if (ReplacementA->isDecltypeType() && + ReplacementB->isDecltypeType()) { + assert(ReplacementA->isDependentType() && + ReplacementB->isDependentType()); + continue; + } + } + llvm::FoldingSetNodeID IDA, IDB; - C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument()) - .Profile(IDA, C); - C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument()) - .Profile(IDB, C); + C.getCanonicalTemplateArgument(ArgA).Profile(IDA, C); + C.getCanonicalTemplateArgument(ArgB).Profile(IDB, C); if (IDA != IDB) return false; } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5138,21 +5138,22 @@ Arg = TemplateArgument(ArgType); } else if (auto *NTTP = dyn_cast(Param)) { - QualType T = - NTTP->getType().getNonPackExpansionType().getNonLValueExprType(*this); + QualType NTTPType = NTTP->getType(); + QualType T = NTTPType.getNonPackExpansionType().getNonLValueExprType(*this); // For class NTTPs, ensure we include the 'const' so the type matches that // of a real template argument. // FIXME: It would be more faithful to model this as something like an // lvalue-to-rvalue conversion applied to a const-qualified lvalue. if (T->isRecordType()) T.addConst(); - Expr *E = new (*this) DeclRefExpr( - *this, NTTP, /*enclosing*/ false, T, - Expr::getValueKindForType(NTTP->getType()), NTTP->getLocation()); + Expr *E = new (*this) + DeclRefExpr(*this, NTTP, /*enclosing*/ false, T, + Expr::getValueKindForType(NTTPType), NTTP->getLocation()); if (NTTP->isParameterPack()) - E = new (*this) PackExpansionExpr(DependentTy, E, NTTP->getLocation(), - None); + E = new (*this) PackExpansionExpr( + NTTPType->isUndeducedAutoType() ? NTTPType : DependentTy, E, + NTTP->getLocation(), None); Arg = TemplateArgument(E); } else { auto *TTP = cast(Param); diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -343,6 +343,22 @@ SETraits::getDecl(Entry)); } +ArrayRef RedeclarableTemplateDecl::getInjectedTemplateArgs() { + TemplateParameterList *Params = getTemplateParameters(); + auto *CommonPtr = getCommonPtr(); + if (!CommonPtr->InjectedArgs) { + auto &Context = getASTContext(); + SmallVector 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 //===----------------------------------------------------------------------===// @@ -393,22 +409,6 @@ InsertPos); } -ArrayRef FunctionTemplateDecl::getInjectedTemplateArgs() { - TemplateParameterList *Params = getTemplateParameters(); - Common *CommonPtr = getCommonPtr(); - if (!CommonPtr->InjectedArgs) { - auto &Context = getASTContext(); - SmallVector 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 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -816,6 +816,17 @@ // - 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(E)) { + assert(FoldE->isRightFold() && FoldE->getOperator() == BO_LAnd); + 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 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -9661,19 +9661,13 @@ /// 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(Cand1.Function->getFunctionType()); auto *PT2 = cast(Cand2.Function->getFunctionType()); if (PT1->getNumParams() == PT2->getNumParams() && @@ -9916,15 +9910,14 @@ isa(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 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1264,8 +1264,7 @@ // unclear in the wording right now. DeclRefExpr *Ref = BuildDeclRefExpr(NTTP, NTTP->getType(), VK_PRValue, NTTP->getLocation()); - if (!Ref) - return true; + assert(Ref); ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint( *this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(), TL.getNamedConcept(), TL.getLAngleLoc(), TL.getRAngleLoc(), @@ -7503,10 +7502,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 @@ -7710,10 +7709,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) { @@ -7763,31 +7762,37 @@ = dyn_cast(Old)) { NonTypeTemplateParmDecl *NewNTTP = cast(New); + // During partial ordering, two auto types are equivalent, ignoring their + // type constraints. + bool CompareAutoWhenPartial = PartialOrdering && + OldNTTP->getType()->isUndeducedAutoType() && + NewNTTP->getType()->isUndeducedAutoType(); // If we are matching a template template argument to a template // template parameter and one of the non-type template parameter types // is dependent, then we must wait until template instantiation time // to actually compare the arguments. - if (Kind != Sema::TPL_TemplateTemplateArgumentMatch || - (!OldNTTP->getType()->isDependentType() && - !NewNTTP->getType()->isDependentType())) - if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) { - if (Complain) { - unsigned NextDiag = diag::err_template_nontype_parm_different_type; - if (TemplateArgLoc.isValid()) { - S.Diag(TemplateArgLoc, - diag::err_template_arg_template_params_mismatch); - NextDiag = diag::note_template_nontype_parm_different_type; - } - S.Diag(NewNTTP->getLocation(), NextDiag) - << NewNTTP->getType() - << (Kind != Sema::TPL_TemplateMatch); - S.Diag(OldNTTP->getLocation(), - diag::note_template_nontype_parm_prev_declaration) - << OldNTTP->getType(); + bool NonDependentTypes = Kind != Sema::TPL_TemplateTemplateArgumentMatch || + (!OldNTTP->getType()->isDependentType() && + !NewNTTP->getType()->isDependentType()); + + if (!CompareAutoWhenPartial && NonDependentTypes && + !S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) { + if (Complain) { + unsigned NextDiag = diag::err_template_nontype_parm_different_type; + if (TemplateArgLoc.isValid()) { + S.Diag(TemplateArgLoc, + diag::err_template_arg_template_params_mismatch); + NextDiag = diag::note_template_nontype_parm_different_type; } - - return false; + S.Diag(NewNTTP->getLocation(), NextDiag) + << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch); + S.Diag(OldNTTP->getLocation(), + diag::note_template_nontype_parm_prev_declaration) + << OldNTTP->getType(); } + + return false; + } } // For template template parameters, check the template parameter types. // The template parameter lists of template template @@ -7801,9 +7806,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(); @@ -7888,7 +7894,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, @@ -7918,8 +7925,8 @@ return false; } - if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, - Kind, TemplateArgLoc)) + if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, Kind, + TemplateArgLoc, PartialOrdering)) return false; ++NewParm; @@ -7934,8 +7941,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; } } @@ -7949,7 +7956,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 @@ -5174,50 +5174,36 @@ /// 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 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; + + TemplateParameterList *TPL1 = FT1->getTemplateParameters(); + TemplateParameterList *TPL2 = FT2->getTemplateParameters(); + FunctionDecl *FD1 = FT1->getTemplatedDecl(); + FunctionDecl *FD2 = FT2->getTemplatedDecl(); // C++ [temp.deduct.partial]p11: // ... 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. - FunctionDecl *FD1 = FT1->getTemplatedDecl(); - FunctionDecl *FD2 = FT2->getTemplatedDecl(); unsigned NumParams1 = FD1->getNumParams(); unsigned NumParams2 = FD2->getNumParams(); bool Variadic1 = NumParams1 && FD1->parameters().back()->isParameterPack(); @@ -5229,7 +5215,59 @@ return FT1; } - return JudgeByConstraints(); + if (!Context.getLangOpts().CPlusPlus20 || isa(FD1) || + isa(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 differ in length, neither template is more specialized + // than the other. + 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 (FD1->getParamDecl(i)->getType().getCanonicalType() != + FD2->getParamDecl(i)->getType().getCanonicalType()) + 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 && FD1->getReturnType().getCanonicalType() != + FD2->getReturnType().getCanonicalType()) + 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; } /// Determine if the two templates are equivalent. @@ -5407,7 +5445,7 @@ } 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 ::value, bool> = true> + bool operator()(T1 *PS1, T2 *PS2) { + // Compare two Template Argument lists using + // AtomicConstraint::hasMatchingParameterMapping. + AtomicConstraint Atomic1(S, nullptr); + Atomic1.ParameterMapping.emplace( + PS1->getTemplateArgsAsWritten()->arguments()); + AtomicConstraint Atomic2(S, nullptr); + Atomic2.ParameterMapping.emplace( + PS2->getTemplateArgsAsWritten()->arguments()); + return Atomic1.hasMatchingParameterMapping(S.getASTContext(), Atomic2); + } + + template ::value, bool> = true> + bool operator()(T1 *Spec, T2 *Primary) { + SmallVector Args; + for (const TemplateArgument &TA : Primary->getInjectedTemplateArgs()) { + // A injected template argument for a parameter pack is + // TemplateArgument::Pack. However, the TemplateArgsAsWritten + // represents a template argument for a parameter pack as + // TemplateArgument::Type with PackExpansionType. Transform the former + // to the same TemplateArgument kind as the latter. + if (TA.getKind() == TemplateArgument::Pack) { + assert(TA.pack_size() == 1); + Args.emplace_back(*TA.pack_begin(), TemplateArgumentLocInfo()); + } else { + Args.emplace_back(TA, TemplateArgumentLocInfo()); + } + } + + AtomicConstraint Atomic1(S, nullptr); + Atomic1.ParameterMapping.emplace(Args); + AtomicConstraint Atomic2(S, nullptr); + Atomic2.ParameterMapping.emplace( + Spec->getTemplateArgsAsWritten()->arguments()); + return Atomic1.hasMatchingParameterMapping(S.getASTContext(), Atomic2); + } +}; } // namespace /// Returns the more specialized template specialization between T1/P1 and @@ -5460,53 +5544,83 @@ 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) { - bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver15; - if (!ClangABICompat15) { - // Consider this a fix for CWG1432. Similar to the fix for CWG1395. - auto *TST1 = T1->castAs(); - auto *TST2 = T2->castAs(); - if (TST1->getNumArgs()) { - 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; - } + TemplateParameterList *TPL1 = P1->getTemplateParameters(); + TemplateParameterList *TPL2 = P2->getTemplateParameters(); + + bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver15; + if (!ClangABICompat15) { + // Consider this a fix for CWG1432. Similar to the fix for CWG1395. + auto *TST1 = T1->castAs(); + auto *TST2 = T2->castAs(); + if (TST1->getNumArgs()) { + 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 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: + + 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)(P1, P2)) + return nullptr; + + llvm::SmallVector 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 @@ -5566,17 +5680,11 @@ 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 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 --- 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 @@ static_assert(is_same_v()), void>); // expected-error {{call to 'bar' is ambiguous}} template - constexpr int goo(int a) requires AtLeast2 && true { + constexpr int goo(int a) requires AtLeast2 && true { // expected-note {{candidate function}} return 1; } template - constexpr int goo(const int b) requires AtLeast2 { + constexpr int goo(const int b) requires AtLeast2 { // 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 - constexpr int doo(int a, ...) requires AtLeast2 && true { // expected-note {{candidate function}} + constexpr int doo(int a, ...) requires AtLeast2 && true { return 1; } template - constexpr int doo(int b) requires AtLeast2 { // expected-note {{candidate function}} + constexpr int doo(int b) requires AtLeast2 { return 2; } - static_assert(goo(1) == 1); - static_assert(doo(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 different types. + static_assert(goo(1) == 1); // expected-error {{call to 'goo' is ambiguous}} + static_assert(doo(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 --- 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,17 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s -struct A; -struct B; - template constexpr bool True = true; template concept C = True; +template concept D = C && sizeof(T) > 2; +template concept E = D && alignof(T) > 1; + +struct A {}; +template struct S {}; +template struct X {}; + +namespace p6 { + +struct B; void f(C auto &, auto &) = delete; template void f(Q &, C auto &); @@ -13,14 +20,85 @@ f(*ap, *bp); } -template struct X {}; - +#if 0 +// FIXME: [temp.func.order]p6.2.1 is not implemented, matching GCC. template bool operator==(X, V) = delete; template bool operator==(T, X); bool h() { return X{} == 0; } +#endif + +template class U, typename... Z> +void foo(T, U) = delete; +template class U, typename... Z> +void foo(T, U) = delete; +template class U, typename... Z> +void foo(T, U); + +void bar(S s) { + foo(0, s); +} + +} // namespace p6 + +namespace TestConversionFunction { +struct Y { + template operator X(); // expected-note {{candidate function [with T = int, U = int]}} + template operator X(); // expected-note {{candidate function [with T = int, U = int]}} +}; + +X f() { + return Y{}; // expected-error {{conversion from 'Y' to 'X' is ambiguous}} +} +} + +namespace ClassPartialSpecPartialOrdering { +template struct Y { Y()=delete; }; // expected-note {{template is declared here}} +template struct Y {}; // expected-error {{class template partial specialization is not more specialized than the primary template}} + +template struct B { B()=delete; }; +template struct B { B()=delete; }; +template struct B {}; + +template class U, typename... Z> +struct Some { Some()=delete; }; +template class U, typename... Z> +struct Some { Some()=delete; }; +template class U, typename... Z> +struct Some {}; + +void f() { + B b; + Some c; +} + +template struct Z; // expected-note {{template is declared here}} +template struct Z; // expected-error {{class template partial specialization is not more specialized than the primary template}} + +template struct W1; +template struct W1 {}; + +template struct W2; +template struct W2 {}; + +template +concept C1 = C && C; +template +concept D1 = D && C; + +template auto T> struct W3; +template auto T> struct W3 {}; + +template auto... T> struct W4; +template auto... T> struct W4 {}; + +struct W1<0> w1; +struct W2<0> w2; +struct W3<0> w3; +struct W4<0> w4; +} namespace PR53640 { diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -967,7 +967,7 @@ P2113R0 - No + Clang 16