Skip to content

Commit 340eac0

Browse files
author
Yuanfang Chen
committed
[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] #49308 [3] https://lists.isocpp.org/core/2020/06/index.php#msg9392 Fixes #54039 Fixes #49308 (PR49964) Reviewed By: royjacobson, #clang-language-wg, mizvekov Differential Revision: https://reviews.llvm.org/D128750
1 parent 9f8340e commit 340eac0

File tree

11 files changed

+385
-144
lines changed

11 files changed

+385
-144
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,9 @@ C++20 Feature Support
534534
which removes the requirement for the ``typename`` keyword in certain contexts.
535535
- Implemented The Equality Operator You Are Looking For (`P2468 <http://wg21.link/p2468r2>`_).
536536

537+
- Implemented `P2113R0: Proposed resolution for 2019 comment CA 112 <https://wg21.link/P2113R0>`_
538+
([temp.func.order]p6.2.1 is not implemented, matching GCC).
539+
537540
C++2b Feature Support
538541
^^^^^^^^^^^^^^^^^^^^^
539542

clang/include/clang/AST/DeclTemplate.h

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,15 @@ class RedeclarableTemplateDecl : public TemplateDecl,
847847
/// The first value in the array is the number of specializations/partial
848848
/// specializations that follow.
849849
uint32_t *LazySpecializations = nullptr;
850+
851+
/// The set of "injected" template arguments used within this
852+
/// template.
853+
///
854+
/// This pointer refers to the template arguments (there are as
855+
/// many template arguments as template parameaters) for the
856+
/// template, and is allocated lazily, since most templates do not
857+
/// require the use of this information.
858+
TemplateArgument *InjectedArgs = nullptr;
850859
};
851860

852861
/// Pointer to the common data shared by all declarations of this
@@ -954,6 +963,14 @@ class RedeclarableTemplateDecl : public TemplateDecl,
954963
getCommonPtr()->InstantiatedFromMember.setPointer(TD);
955964
}
956965

966+
/// Retrieve the "injected" template arguments that correspond to the
967+
/// template parameters of this template.
968+
///
969+
/// Although the C++ standard has no notion of the "injected" template
970+
/// arguments for a template, the notion is convenient when
971+
/// we need to perform substitutions inside the definition of a template.
972+
ArrayRef<TemplateArgument> getInjectedTemplateArgs();
973+
957974
using redecl_range = redeclarable_base::redecl_range;
958975
using redecl_iterator = redeclarable_base::redecl_iterator;
959976

@@ -998,15 +1015,6 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl {
9981015
/// template, including explicit specializations and instantiations.
9991016
llvm::FoldingSetVector<FunctionTemplateSpecializationInfo> Specializations;
10001017

1001-
/// The set of "injected" template arguments used within this
1002-
/// function template.
1003-
///
1004-
/// This pointer refers to the template arguments (there are as
1005-
/// many template arguments as template parameaters) for the function
1006-
/// template, and is allocated lazily, since most function templates do not
1007-
/// require the use of this information.
1008-
TemplateArgument *InjectedArgs = nullptr;
1009-
10101018
Common() = default;
10111019
};
10121020

@@ -1106,15 +1114,6 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl {
11061114
return makeSpecIterator(getSpecializations(), true);
11071115
}
11081116

1109-
/// Retrieve the "injected" template arguments that correspond to the
1110-
/// template parameters of this function template.
1111-
///
1112-
/// Although the C++ standard has no notion of the "injected" template
1113-
/// arguments for a function template, the notion is convenient when
1114-
/// we need to perform substitutions inside the definition of a function
1115-
/// template.
1116-
ArrayRef<TemplateArgument> getInjectedTemplateArgs();
1117-
11181117
/// Return whether this function template is an abbreviated function template,
11191118
/// e.g. `void foo(auto x)` or `template<typename T> void foo(auto x)`
11201119
bool isAbbreviated() const {

clang/include/clang/Sema/Sema.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8329,14 +8329,17 @@ class Sema final {
83298329
const NamedDecl *NewInstFrom, TemplateParameterList *New,
83308330
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
83318331
TemplateParameterListEqualKind Kind,
8332-
SourceLocation TemplateArgLoc = SourceLocation());
8332+
SourceLocation TemplateArgLoc = SourceLocation(),
8333+
bool PartialOrdering = false);
83338334

83348335
bool TemplateParameterListsAreEqual(
83358336
TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
83368337
TemplateParameterListEqualKind Kind,
8337-
SourceLocation TemplateArgLoc = SourceLocation()) {
8338+
SourceLocation TemplateArgLoc = SourceLocation(),
8339+
bool PartialOrdering = false) {
83388340
return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain,
8339-
Kind, TemplateArgLoc);
8341+
Kind, TemplateArgLoc,
8342+
PartialOrdering);
83408343
}
83418344

83428345
bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams);
@@ -9021,8 +9024,7 @@ class Sema final {
90219024
FunctionTemplateDecl *getMoreSpecializedTemplate(
90229025
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
90239026
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
9024-
unsigned NumCallArguments2, bool Reversed = false,
9025-
bool AllowOrderingByConstraints = true);
9027+
unsigned NumCallArguments2, bool Reversed = false);
90269028
UnresolvedSetIterator
90279029
getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
90289030
TemplateSpecCandidateSet &FailedCandidates,

clang/lib/AST/DeclTemplate.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,22 @@ void RedeclarableTemplateDecl::addSpecializationImpl(
353353
SETraits::getDecl(Entry));
354354
}
355355

356+
ArrayRef<TemplateArgument> RedeclarableTemplateDecl::getInjectedTemplateArgs() {
357+
TemplateParameterList *Params = getTemplateParameters();
358+
auto *CommonPtr = getCommonPtr();
359+
if (!CommonPtr->InjectedArgs) {
360+
auto &Context = getASTContext();
361+
SmallVector<TemplateArgument, 16> TemplateArgs;
362+
Context.getInjectedTemplateArgs(Params, TemplateArgs);
363+
CommonPtr->InjectedArgs =
364+
new (Context) TemplateArgument[TemplateArgs.size()];
365+
std::copy(TemplateArgs.begin(), TemplateArgs.end(),
366+
CommonPtr->InjectedArgs);
367+
}
368+
369+
return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size());
370+
}
371+
356372
//===----------------------------------------------------------------------===//
357373
// FunctionTemplateDecl Implementation
358374
//===----------------------------------------------------------------------===//
@@ -403,22 +419,6 @@ void FunctionTemplateDecl::addSpecialization(
403419
InsertPos);
404420
}
405421

406-
ArrayRef<TemplateArgument> FunctionTemplateDecl::getInjectedTemplateArgs() {
407-
TemplateParameterList *Params = getTemplateParameters();
408-
Common *CommonPtr = getCommonPtr();
409-
if (!CommonPtr->InjectedArgs) {
410-
auto &Context = getASTContext();
411-
SmallVector<TemplateArgument, 16> TemplateArgs;
412-
Context.getInjectedTemplateArgs(Params, TemplateArgs);
413-
CommonPtr->InjectedArgs =
414-
new (Context) TemplateArgument[TemplateArgs.size()];
415-
std::copy(TemplateArgs.begin(), TemplateArgs.end(),
416-
CommonPtr->InjectedArgs);
417-
}
418-
419-
return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size());
420-
}
421-
422422
void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
423423
using Base = RedeclarableTemplateDecl;
424424

clang/lib/Sema/SemaConcept.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,18 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
11041104
// - The normal form of an expression (E) is the normal form of E.
11051105
// [...]
11061106
E = E->IgnoreParenImpCasts();
1107+
1108+
// C++2a [temp.param]p4:
1109+
// [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
1110+
//
1111+
// Using the pattern suffices because the partial ordering rules guarantee
1112+
// the template paramaters are equivalent.
1113+
if (auto *FoldE = dyn_cast<const CXXFoldExpr>(E)) {
1114+
assert(FoldE->isRightFold() && FoldE->getOperator() == BO_LAnd);
1115+
assert(E->IgnoreParenImpCasts() == E);
1116+
E = FoldE->getPattern();
1117+
}
1118+
11071119
if (LogicalBinOp BO = E) {
11081120
auto LHS = fromConstraintExpr(S, D, BO.getLHS());
11091121
if (!LHS)

clang/lib/Sema/SemaOverload.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9776,19 +9776,13 @@ static bool haveSameParameterTypes(ASTContext &Context, const FunctionDecl *F1,
97769776

97779777
/// We're allowed to use constraints partial ordering only if the candidates
97789778
/// have the same parameter types:
9779-
/// [temp.func.order]p6.2.2 [...] or if the function parameters that
9780-
/// positionally correspond between the two templates are not of the same type,
9781-
/// neither template is more specialized than the other.
97829779
/// [over.match.best]p2.6
97839780
/// F1 and F2 are non-template functions with the same parameter-type-lists,
97849781
/// and F1 is more constrained than F2 [...]
9785-
static bool canCompareFunctionConstraints(Sema &S,
9782+
static bool sameFunctionParameterTypeLists(Sema &S,
97869783
const OverloadCandidate &Cand1,
97879784
const OverloadCandidate &Cand2) {
9788-
// FIXME: Per P2113R0 we also need to compare the template parameter lists
9789-
// when comparing template functions.
9790-
if (Cand1.Function && Cand2.Function && Cand1.Function->hasPrototype() &&
9791-
Cand2.Function->hasPrototype()) {
9785+
if (Cand1.Function && Cand2.Function) {
97929786
auto *PT1 = cast<FunctionProtoType>(Cand1.Function->getFunctionType());
97939787
auto *PT2 = cast<FunctionProtoType>(Cand2.Function->getFunctionType());
97949788
if (PT1->getNumParams() == PT2->getNumParams() &&
@@ -10031,15 +10025,14 @@ bool clang::isBetterOverloadCandidate(
1003110025
isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
1003210026
: TPOC_Call,
1003310027
Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
10034-
Cand1.isReversed() ^ Cand2.isReversed(),
10035-
canCompareFunctionConstraints(S, Cand1, Cand2)))
10028+
Cand1.isReversed() ^ Cand2.isReversed()))
1003610029
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
1003710030
}
1003810031

1003910032
// -— F1 and F2 are non-template functions with the same
1004010033
// parameter-type-lists, and F1 is more constrained than F2 [...],
1004110034
if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
10042-
canCompareFunctionConstraints(S, Cand1, Cand2)) {
10035+
sameFunctionParameterTypeLists(S, Cand1, Cand2)) {
1004310036
Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
1004410037
Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
1004510038
if (RC1 && RC2) {

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7652,10 +7652,10 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
76527652

76537653
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
76547654
Arg.getLocation())) {
7655-
// C++2a[temp.func.order]p2
7655+
// P2113
7656+
// C++20[temp.func.order]p2
76567657
// [...] If both deductions succeed, the partial ordering selects the
7657-
// more constrained template as described by the rules in
7658-
// [temp.constr.order].
7658+
// more constrained template (if one exists) as determined below.
76597659
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
76607660
Params->getAssociatedConstraints(ParamsAC);
76617661
// C++2a[temp.arg.template]p3
@@ -7864,7 +7864,8 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
78647864
static bool MatchTemplateParameterKind(
78657865
Sema &S, NamedDecl *New, const NamedDecl *NewInstFrom, NamedDecl *Old,
78667866
const NamedDecl *OldInstFrom, bool Complain,
7867-
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
7867+
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc,
7868+
bool PartialOrdering) {
78687869
// Check the actual kind (type, non-type, template).
78697870
if (Old->getKind() != New->getKind()) {
78707871
if (Complain) {
@@ -7952,11 +7953,11 @@ static bool MatchTemplateParameterKind(
79527953
(Kind == Sema::TPL_TemplateMatch
79537954
? Sema::TPL_TemplateTemplateParmMatch
79547955
: Kind),
7955-
TemplateArgLoc))
7956+
TemplateArgLoc, PartialOrdering))
79567957
return false;
79577958
}
79587959

7959-
if (Kind != Sema::TPL_TemplateTemplateArgumentMatch &&
7960+
if (!PartialOrdering && Kind != Sema::TPL_TemplateTemplateArgumentMatch &&
79607961
!isa<TemplateTemplateParmDecl>(Old)) {
79617962
const Expr *NewC = nullptr, *OldC = nullptr;
79627963

@@ -8049,7 +8050,8 @@ void DiagnoseTemplateParameterListArityMismatch(Sema &S,
80498050
bool Sema::TemplateParameterListsAreEqual(
80508051
const NamedDecl *NewInstFrom, TemplateParameterList *New,
80518052
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
8052-
TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
8053+
TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc,
8054+
bool PartialOrdering) {
80538055
if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) {
80548056
if (Complain)
80558057
DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
@@ -8081,7 +8083,7 @@ bool Sema::TemplateParameterListsAreEqual(
80818083

80828084
if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
80838085
OldInstFrom, Complain, Kind,
8084-
TemplateArgLoc))
8086+
TemplateArgLoc, PartialOrdering))
80858087
return false;
80868088

80878089
++NewParm;
@@ -8098,7 +8100,7 @@ bool Sema::TemplateParameterListsAreEqual(
80988100
for (; NewParm != NewParmEnd; ++NewParm) {
80998101
if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
81008102
OldInstFrom, Complain, Kind,
8101-
TemplateArgLoc))
8103+
TemplateArgLoc, PartialOrdering))
81028104
return false;
81038105
}
81048106
}
@@ -8112,7 +8114,7 @@ bool Sema::TemplateParameterListsAreEqual(
81128114
return false;
81138115
}
81148116

8115-
if (Kind != TPL_TemplateTemplateArgumentMatch) {
8117+
if (!PartialOrdering && Kind != TPL_TemplateTemplateArgumentMatch) {
81168118
const Expr *NewRC = New->getRequiresClause();
81178119
const Expr *OldRC = Old->getRequiresClause();
81188120

0 commit comments

Comments
 (0)