Skip to content

Commit 63afcbb

Browse files
authored
[clang][Sema] Bugfix for choosing the more specialized overload (#83279)
There was a bug in Clang where it couldn't choose which overload candidate was more specialized if it was comparing a member-function to a non-member function. Previously, this was detected as an ambiguity, now Clang chooses correctly. This patch fixes the bug by fully implementing CWG2445 and moving the template transformation described in `[temp.func.order]` paragraph 3 from `isAtLeastAsSpecializedAs()` to `Sema::getMoreSpecializedTemplate()` so we have the transformed parameter list during the whole comparison. Also, to be able to add the correct type for the implicit object parameter `Sema::getMoreSpecializedTemplate()` has new parameters for the object type. Fixes #74494, fixes #82509
1 parent a8dd99e commit 63afcbb

File tree

8 files changed

+273
-110
lines changed

8 files changed

+273
-110
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,12 @@ Bug Fixes to C++ Support
319319
Fixes (#GH80630)
320320
- Fix a crash when an explicit template argument list is used with a name for which lookup
321321
finds a non-template function and a dependent using declarator.
322+
- Fix a bug where overload resolution falsely reported an ambiguity when it was comparing
323+
a member-function against a non member function or a member-function with an
324+
explicit object parameter against a member function with no explicit object parameter
325+
when one of the function had more specialized templates.
326+
Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_)
327+
and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_)
322328

323329
Bug Fixes to AST Handling
324330
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9952,7 +9952,8 @@ class Sema final {
99529952
FunctionTemplateDecl *getMoreSpecializedTemplate(
99539953
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
99549954
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
9955-
unsigned NumCallArguments2, bool Reversed = false);
9955+
QualType RawObj1Ty = {}, QualType RawObj2Ty = {}, bool Reversed = false);
9956+
99569957
UnresolvedSetIterator
99579958
getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
99589959
TemplateSpecCandidateSet &FailedCandidates,

clang/lib/Sema/SemaOverload.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10571,14 +10571,23 @@ bool clang::isBetterOverloadCandidate(
1057110571
// according to the partial ordering rules described in 14.5.5.2, or,
1057210572
// if not that,
1057310573
if (Cand1IsSpecialization && Cand2IsSpecialization) {
10574+
const auto *Obj1Context =
10575+
dyn_cast<CXXRecordDecl>(Cand1.FoundDecl->getDeclContext());
10576+
const auto *Obj2Context =
10577+
dyn_cast<CXXRecordDecl>(Cand2.FoundDecl->getDeclContext());
1057410578
if (FunctionTemplateDecl *BetterTemplate = S.getMoreSpecializedTemplate(
1057510579
Cand1.Function->getPrimaryTemplate(),
1057610580
Cand2.Function->getPrimaryTemplate(), Loc,
1057710581
isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
1057810582
: TPOC_Call,
10579-
Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
10580-
Cand1.isReversed() ^ Cand2.isReversed()))
10583+
Cand1.ExplicitCallArguments,
10584+
Obj1Context ? QualType(Obj1Context->getTypeForDecl(), 0)
10585+
: QualType{},
10586+
Obj2Context ? QualType(Obj2Context->getTypeForDecl(), 0)
10587+
: QualType{},
10588+
Cand1.isReversed() ^ Cand2.isReversed())) {
1058110589
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
10590+
}
1058210591
}
1058310592

1058410593
// -— F1 and F2 are non-template functions with the same

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 130 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -5333,38 +5333,38 @@ bool Sema::CheckIfFunctionSpecializationIsImmediate(FunctionDecl *FD,
53335333
return false;
53345334
}
53355335

5336-
/// If this is a non-static member function,
5337-
static void
5338-
AddImplicitObjectParameterType(ASTContext &Context,
5339-
CXXMethodDecl *Method,
5340-
SmallVectorImpl<QualType> &ArgTypes) {
5341-
// C++11 [temp.func.order]p3:
5342-
// [...] The new parameter is of type "reference to cv A," where cv are
5343-
// the cv-qualifiers of the function template (if any) and A is
5344-
// the class of which the function template is a member.
5336+
static QualType GetImplicitObjectParameterType(ASTContext &Context,
5337+
const CXXMethodDecl *Method,
5338+
QualType RawType,
5339+
bool IsOtherRvr) {
5340+
// C++20 [temp.func.order]p3.1, p3.2:
5341+
// - The type X(M) is "rvalue reference to cv A" if the optional
5342+
// ref-qualifier of M is && or if M has no ref-qualifier and the
5343+
// positionally-corresponding parameter of the other transformed template
5344+
// has rvalue reference type; if this determination depends recursively
5345+
// upon whether X(M) is an rvalue reference type, it is not considered to
5346+
// have rvalue reference type.
53455347
//
5346-
// The standard doesn't say explicitly, but we pick the appropriate kind of
5347-
// reference type based on [over.match.funcs]p4.
5348-
assert(Method && Method->isImplicitObjectMemberFunction() &&
5349-
"expected an implicit objet function");
5350-
QualType ArgTy = Context.getTypeDeclType(Method->getParent());
5351-
ArgTy = Context.getQualifiedType(ArgTy, Method->getMethodQualifiers());
5352-
if (Method->getRefQualifier() == RQ_RValue)
5353-
ArgTy = Context.getRValueReferenceType(ArgTy);
5354-
else
5355-
ArgTy = Context.getLValueReferenceType(ArgTy);
5356-
ArgTypes.push_back(ArgTy);
5348+
// - Otherwise, X(M) is "lvalue reference to cv A".
5349+
assert(Method && !Method->isExplicitObjectMemberFunction() &&
5350+
"expected a member function with no explicit object parameter");
5351+
5352+
RawType = Context.getQualifiedType(RawType, Method->getMethodQualifiers());
5353+
if (Method->getRefQualifier() == RQ_RValue ||
5354+
(IsOtherRvr && Method->getRefQualifier() == RQ_None))
5355+
return Context.getRValueReferenceType(RawType);
5356+
return Context.getLValueReferenceType(RawType);
53575357
}
53585358

53595359
/// Determine whether the function template \p FT1 is at least as
53605360
/// specialized as \p FT2.
5361-
static bool isAtLeastAsSpecializedAs(Sema &S,
5362-
SourceLocation Loc,
5363-
FunctionTemplateDecl *FT1,
5364-
FunctionTemplateDecl *FT2,
5361+
static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
5362+
const FunctionTemplateDecl *FT1,
5363+
const FunctionTemplateDecl *FT2,
53655364
TemplatePartialOrderingContext TPOC,
5366-
unsigned NumCallArguments1,
5367-
bool Reversed) {
5365+
bool Reversed,
5366+
const SmallVector<QualType> &Args1,
5367+
const SmallVector<QualType> &Args2) {
53685368
assert(!Reversed || TPOC == TPOC_Call);
53695369

53705370
FunctionDecl *FD1 = FT1->getTemplatedDecl();
@@ -5381,74 +5381,15 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
53815381
// The types used to determine the ordering depend on the context in which
53825382
// the partial ordering is done:
53835383
TemplateDeductionInfo Info(Loc);
5384-
SmallVector<QualType, 4> Args2;
53855384
switch (TPOC) {
5386-
case TPOC_Call: {
5387-
// - In the context of a function call, the function parameter types are
5388-
// used.
5389-
CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1);
5390-
CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2);
5391-
5392-
// C++11 [temp.func.order]p3:
5393-
// [...] If only one of the function templates is a non-static
5394-
// member, that function template is considered to have a new
5395-
// first parameter inserted in its function parameter list. The
5396-
// new parameter is of type "reference to cv A," where cv are
5397-
// the cv-qualifiers of the function template (if any) and A is
5398-
// the class of which the function template is a member.
5399-
//
5400-
// Note that we interpret this to mean "if one of the function
5401-
// templates is a non-static member and the other is a non-member";
5402-
// otherwise, the ordering rules for static functions against non-static
5403-
// functions don't make any sense.
5404-
//
5405-
// C++98/03 doesn't have this provision but we've extended DR532 to cover
5406-
// it as wording was broken prior to it.
5407-
SmallVector<QualType, 4> Args1;
5408-
5409-
unsigned NumComparedArguments = NumCallArguments1;
5410-
5411-
if (!Method2 && Method1 && Method1->isImplicitObjectMemberFunction()) {
5412-
// Compare 'this' from Method1 against first parameter from Method2.
5413-
AddImplicitObjectParameterType(S.Context, Method1, Args1);
5414-
++NumComparedArguments;
5415-
} else if (!Method1 && Method2 &&
5416-
Method2->isImplicitObjectMemberFunction()) {
5417-
// Compare 'this' from Method2 against first parameter from Method1.
5418-
AddImplicitObjectParameterType(S.Context, Method2, Args2);
5419-
} else if (Method1 && Method2 && Reversed &&
5420-
Method1->isImplicitObjectMemberFunction() &&
5421-
Method2->isImplicitObjectMemberFunction()) {
5422-
// Compare 'this' from Method1 against second parameter from Method2
5423-
// and 'this' from Method2 against second parameter from Method1.
5424-
AddImplicitObjectParameterType(S.Context, Method1, Args1);
5425-
AddImplicitObjectParameterType(S.Context, Method2, Args2);
5426-
++NumComparedArguments;
5427-
}
5428-
5429-
Args1.insert(Args1.end(), Proto1->param_type_begin(),
5430-
Proto1->param_type_end());
5431-
Args2.insert(Args2.end(), Proto2->param_type_begin(),
5432-
Proto2->param_type_end());
5433-
5434-
// C++ [temp.func.order]p5:
5435-
// The presence of unused ellipsis and default arguments has no effect on
5436-
// the partial ordering of function templates.
5437-
if (Args1.size() > NumComparedArguments)
5438-
Args1.resize(NumComparedArguments);
5439-
if (Args2.size() > NumComparedArguments)
5440-
Args2.resize(NumComparedArguments);
5441-
if (Reversed)
5442-
std::reverse(Args2.begin(), Args2.end());
5443-
5385+
case TPOC_Call:
54445386
if (DeduceTemplateArguments(S, TemplateParams, Args2.data(), Args2.size(),
54455387
Args1.data(), Args1.size(), Info, Deduced,
54465388
TDF_None, /*PartialOrdering=*/true) !=
54475389
TemplateDeductionResult::Success)
54485390
return false;
54495391

54505392
break;
5451-
}
54525393

54535394
case TPOC_Conversion:
54545395
// - In the context of a call to a conversion operator, the return types
@@ -5536,8 +5477,13 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
55365477
/// \param NumCallArguments1 The number of arguments in the call to FT1, used
55375478
/// only when \c TPOC is \c TPOC_Call.
55385479
///
5539-
/// \param NumCallArguments2 The number of arguments in the call to FT2, used
5540-
/// only when \c TPOC is \c TPOC_Call.
5480+
/// \param RawObj1Ty The type of the object parameter of FT1 if a member
5481+
/// function only used if \c TPOC is \c TPOC_Call and FT1 is a Function
5482+
/// template from a member function
5483+
///
5484+
/// \param RawObj2Ty The type of the object parameter of FT2 if a member
5485+
/// function only used if \c TPOC is \c TPOC_Call and FT2 is a Function
5486+
/// template from a member function
55415487
///
55425488
/// \param Reversed If \c true, exactly one of FT1 and FT2 is an overload
55435489
/// candidate with a reversed parameter order. In this case, the corresponding
@@ -5548,13 +5494,76 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
55485494
FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
55495495
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
55505496
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
5551-
unsigned NumCallArguments2, bool Reversed) {
5497+
QualType RawObj1Ty, QualType RawObj2Ty, bool Reversed) {
5498+
SmallVector<QualType> Args1;
5499+
SmallVector<QualType> Args2;
5500+
const FunctionDecl *FD1 = FT1->getTemplatedDecl();
5501+
const FunctionDecl *FD2 = FT2->getTemplatedDecl();
5502+
bool ShouldConvert1 = false;
5503+
bool ShouldConvert2 = false;
5504+
QualType Obj1Ty;
5505+
QualType Obj2Ty;
5506+
if (TPOC == TPOC_Call) {
5507+
const FunctionProtoType *Proto1 =
5508+
FD1->getType()->getAs<FunctionProtoType>();
5509+
const FunctionProtoType *Proto2 =
5510+
FD2->getType()->getAs<FunctionProtoType>();
5511+
5512+
// - In the context of a function call, the function parameter types are
5513+
// used.
5514+
const CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1);
5515+
const CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2);
5516+
// C++20 [temp.func.order]p3
5517+
// [...] Each function template M that is a member function is
5518+
// considered to have a new first parameter of type
5519+
// X(M), described below, inserted in its function parameter list.
5520+
//
5521+
// Note that we interpret "that is a member function" as
5522+
// "that is a member function with no expicit object argument".
5523+
// Otherwise the ordering rules for methods with expicit objet arguments
5524+
// against anything else make no sense.
5525+
ShouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction();
5526+
ShouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction();
5527+
if (ShouldConvert1) {
5528+
bool IsRValRef2 =
5529+
ShouldConvert2
5530+
? Method2->getRefQualifier() == RQ_RValue
5531+
: Proto2->param_type_begin()[0]->isRValueReferenceType();
5532+
// Compare 'this' from Method1 against first parameter from Method2.
5533+
Obj1Ty = GetImplicitObjectParameterType(this->Context, Method1, RawObj1Ty,
5534+
IsRValRef2);
5535+
Args1.push_back(Obj1Ty);
5536+
}
5537+
if (ShouldConvert2) {
5538+
bool IsRValRef1 =
5539+
ShouldConvert1
5540+
? Method1->getRefQualifier() == RQ_RValue
5541+
: Proto1->param_type_begin()[0]->isRValueReferenceType();
5542+
// Compare 'this' from Method2 against first parameter from Method1.
5543+
Obj2Ty = GetImplicitObjectParameterType(this->Context, Method2, RawObj2Ty,
5544+
IsRValRef1);
5545+
Args2.push_back(Obj2Ty);
5546+
}
5547+
size_t NumComparedArguments = NumCallArguments1 + ShouldConvert1;
5548+
5549+
Args1.insert(Args1.end(), Proto1->param_type_begin(),
5550+
Proto1->param_type_end());
5551+
Args2.insert(Args2.end(), Proto2->param_type_begin(),
5552+
Proto2->param_type_end());
55525553

5553-
bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC,
5554-
NumCallArguments1, Reversed);
5555-
bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC,
5556-
NumCallArguments2, Reversed);
5554+
// C++ [temp.func.order]p5:
5555+
// The presence of unused ellipsis and default arguments has no effect on
5556+
// the partial ordering of function templates.
5557+
Args1.resize(std::min(Args1.size(), NumComparedArguments));
5558+
Args2.resize(std::min(Args2.size(), NumComparedArguments));
55575559

5560+
if (Reversed)
5561+
std::reverse(Args2.begin(), Args2.end());
5562+
}
5563+
bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, Reversed,
5564+
Args1, Args2);
5565+
bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC, Reversed,
5566+
Args2, Args1);
55585567
// C++ [temp.deduct.partial]p10:
55595568
// F is more specialized than G if F is at least as specialized as G and G
55605569
// is not at least as specialized as F.
@@ -5568,12 +5577,28 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
55685577
// ... and if G has a trailing function parameter pack for which F does not
55695578
// have a corresponding parameter, and if F does not have a trailing
55705579
// function parameter pack, then F is more specialized than G.
5571-
FunctionDecl *FD1 = FT1->getTemplatedDecl();
5572-
FunctionDecl *FD2 = FT2->getTemplatedDecl();
5573-
unsigned NumParams1 = FD1->getNumParams();
5574-
unsigned NumParams2 = FD2->getNumParams();
5575-
bool Variadic1 = NumParams1 && FD1->parameters().back()->isParameterPack();
5576-
bool Variadic2 = NumParams2 && FD2->parameters().back()->isParameterPack();
5580+
5581+
SmallVector<QualType> Param1;
5582+
Param1.reserve(FD1->param_size() + ShouldConvert1);
5583+
if (ShouldConvert1)
5584+
Param1.push_back(Obj1Ty);
5585+
for (const auto &P : FD1->parameters())
5586+
Param1.push_back(P->getType());
5587+
5588+
SmallVector<QualType> Param2;
5589+
Param2.reserve(FD2->param_size() + ShouldConvert2);
5590+
if (ShouldConvert2)
5591+
Param2.push_back(Obj2Ty);
5592+
for (const auto &P : FD2->parameters())
5593+
Param2.push_back(P->getType());
5594+
5595+
unsigned NumParams1 = Param1.size();
5596+
unsigned NumParams2 = Param2.size();
5597+
5598+
bool Variadic1 =
5599+
FD1->param_size() && FD1->parameters().back()->isParameterPack();
5600+
bool Variadic2 =
5601+
FD2->param_size() && FD2->parameters().back()->isParameterPack();
55775602
if (Variadic1 != Variadic2) {
55785603
if (Variadic1 && NumParams1 > NumParams2)
55795604
return FT2;
@@ -5584,8 +5609,8 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
55845609
// This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
55855610
// there is no wording or even resolution for this issue.
55865611
for (int i = 0, e = std::min(NumParams1, NumParams2); i < e; ++i) {
5587-
QualType T1 = FD1->getParamDecl(i)->getType().getCanonicalType();
5588-
QualType T2 = FD2->getParamDecl(i)->getType().getCanonicalType();
5612+
QualType T1 = Param1[i].getCanonicalType();
5613+
QualType T2 = Param2[i].getCanonicalType();
55895614
auto *TST1 = dyn_cast<TemplateSpecializationType>(T1);
55905615
auto *TST2 = dyn_cast<TemplateSpecializationType>(T2);
55915616
if (!TST1 || !TST2)
@@ -5644,8 +5669,7 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
56445669
// Any top-level cv-qualifiers modifying a parameter type are deleted when
56455670
// forming the function type.
56465671
for (unsigned i = 0; i < NumParams1; ++i)
5647-
if (!Context.hasSameUnqualifiedType(FD1->getParamDecl(i)->getType(),
5648-
FD2->getParamDecl(i)->getType()))
5672+
if (!Context.hasSameUnqualifiedType(Param1[i], Param2[i]))
56495673
return nullptr;
56505674

56515675
// C++20 [temp.func.order]p6.3:
@@ -5733,8 +5757,8 @@ UnresolvedSetIterator Sema::getMostSpecialized(
57335757
FunctionTemplateDecl *Challenger
57345758
= cast<FunctionDecl>(*I)->getPrimaryTemplate();
57355759
assert(Challenger && "Not a function template specialization?");
5736-
if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger,
5737-
Loc, TPOC_Other, 0, 0),
5760+
if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, Loc,
5761+
TPOC_Other, 0),
57385762
Challenger)) {
57395763
Best = I;
57405764
BestTemplate = Challenger;
@@ -5749,7 +5773,7 @@ UnresolvedSetIterator Sema::getMostSpecialized(
57495773
= cast<FunctionDecl>(*I)->getPrimaryTemplate();
57505774
if (I != Best &&
57515775
!isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger,
5752-
Loc, TPOC_Other, 0, 0),
5776+
Loc, TPOC_Other, 0),
57535777
BestTemplate)) {
57545778
Ambiguous = true;
57555779
break;

0 commit comments

Comments
 (0)