[clang] [Clang] Correctly handle taking the address of an explicit object member function template (PR #147046)
Corentin Jabot via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 10 05:05:17 PDT 2025
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/147046
>From a54b4f8120460c4374422c9847c22aed0ef8665f Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 4 Jul 2025 12:16:02 +0200
Subject: [PATCH 1/2] [Clang] Correctly handle taking the address of an
explicit object member function template
When implementing #93430, I failed to consider some cases
involving function templates.
```
struct A {
template <typename T>
void a(this T self);
};
(&A::a<A>)(A{});
```
This fixes that
---
clang/docs/ReleaseNotes.rst | 2 +
clang/include/clang/Sema/Sema.h | 1 +
clang/lib/Sema/SemaOverload.cpp | 77 ++++++++-----
clang/lib/Sema/SemaTemplateDeduction.cpp | 11 +-
clang/test/CXX/drs/cwg26xx.cpp | 25 ++++-
clang/test/SemaCXX/cxx2b-deducing-this.cpp | 122 +++++++++++++++++++++
6 files changed, 205 insertions(+), 33 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3d893e0aa8e2c..b238512920f63 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -896,6 +896,8 @@ Bug Fixes to C++ Support
- Fixed a crash when constant evaluating some explicit object member assignment operators. (#GH142835)
- Fixed an access checking bug when substituting into concepts (#GH115838)
- Fix a bug where private access specifier of overloaded function not respected. (#GH107629)
+- Correctly handles calling an explicit object member function template overload set
+ through its address (``(&Foo::bar<baz>)()``).
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3fe26f950ad51..8b1eced359404 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12500,6 +12500,7 @@ class Sema final : public SemaBase {
sema::TemplateDeductionInfo &Info,
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs,
bool PartialOverloading, bool PartialOrdering,
+ bool ForOverloadSetAddressResolution,
llvm::function_ref<bool(bool)> CheckNonDependent =
[](bool /*OnlyInitializeNonUserDefinedConversions*/) {
return false;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 34f512b13d6a8..f72da2b59f42e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7700,18 +7700,6 @@ void Sema::AddMethodCandidate(
EnterExpressionEvaluationContext Unevaluated(
*this, Sema::ExpressionEvaluationContext::Unevaluated);
- // Add this candidate
- OverloadCandidate &Candidate =
- CandidateSet.addCandidate(Args.size() + 1, EarlyConversions);
- Candidate.FoundDecl = FoundDecl;
- Candidate.Function = Method;
- Candidate.RewriteKind =
- CandidateSet.getRewriteInfo().getRewriteKind(Method, PO);
- Candidate.TookAddressOfOverload =
- CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet;
- Candidate.ExplicitCallArguments = Args.size();
- Candidate.StrictPackMatch = StrictPackMatch;
-
bool IgnoreExplicitObject =
(Method->isExplicitObjectMemberFunction() &&
CandidateSet.getKind() ==
@@ -7727,6 +7715,23 @@ void Sema::AddMethodCandidate(
unsigned NumParams = Method->getNumParams() - ExplicitOffset +
int(ImplicitObjectMethodTreatedAsStatic);
+ unsigned ExtraArgs =
+ CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet
+ ? 0
+ : 1;
+
+ // Add this candidate
+ OverloadCandidate &Candidate =
+ CandidateSet.addCandidate(Args.size() + ExtraArgs, EarlyConversions);
+ Candidate.FoundDecl = FoundDecl;
+ Candidate.Function = Method;
+ Candidate.RewriteKind =
+ CandidateSet.getRewriteInfo().getRewriteKind(Method, PO);
+ Candidate.TookAddressOfOverload =
+ CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet;
+ Candidate.ExplicitCallArguments = Args.size();
+ Candidate.StrictPackMatch = StrictPackMatch;
+
// (C++ 13.3.2p2): A candidate function having fewer than m
// parameters is viable only if it has an ellipsis in its parameter
// list (8.3.5).
@@ -7757,7 +7762,9 @@ void Sema::AddMethodCandidate(
Candidate.Viable = true;
unsigned FirstConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0;
- if (ObjectType.isNull())
+ if (IgnoreExplicitObject)
+ ;
+ else if (ObjectType.isNull())
Candidate.IgnoreObjectArgument = true;
else if (Method->isStatic()) {
// [over.best.ics.general]p8
@@ -7807,7 +7814,7 @@ void Sema::AddMethodCandidate(
// arguments.
for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) {
unsigned ConvIdx =
- PO == OverloadCandidateParamOrder::Reversed ? 0 : (ArgIdx + 1);
+ PO == OverloadCandidateParamOrder::Reversed ? 0 : (ArgIdx + ExtraArgs);
if (Candidate.Conversions[ConvIdx].isInitialized()) {
// We already formed a conversion sequence for this parameter during
// template argument deduction.
@@ -7876,6 +7883,7 @@ static void AddMethodTemplateCandidateImmediately(
// function template are combined with the set of non-template candidate
// functions.
TemplateDeductionInfo Info(CandidateSet.getLocation());
+ auto Method = cast<CXXMethodDecl>(MethodTmpl->getTemplatedDecl());
FunctionDecl *Specialization = nullptr;
ConversionSequenceList Conversions;
if (TemplateDeductionResult Result = S.DeduceTemplateArguments(
@@ -7897,14 +7905,18 @@ static void AddMethodTemplateCandidateImmediately(
OverloadCandidate &Candidate =
CandidateSet.addCandidate(Conversions.size(), Conversions);
Candidate.FoundDecl = FoundDecl;
- Candidate.Function = MethodTmpl->getTemplatedDecl();
+ Candidate.Function = Method;
Candidate.Viable = false;
Candidate.RewriteKind =
CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
Candidate.IsSurrogate = false;
+ Candidate.TookAddressOfOverload =
+ CandidateSet.getKind() ==
+ OverloadCandidateSet::CSK_AddressOfOverloadSet;
+
Candidate.IgnoreObjectArgument =
- cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||
- ObjectType.isNull();
+ Method->isStatic() ||
+ (!Method->isExplicitObjectMemberFunction() && ObjectType.isNull());
Candidate.ExplicitCallArguments = Args.size();
if (Result == TemplateDeductionResult::NonDependentConversionFailure)
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -8024,9 +8036,16 @@ static void AddTemplateOverloadCandidateImmediately(
Candidate.IsADLCandidate = llvm::to_underlying(IsADLCandidate);
// Ignore the object argument if there is one, since we don't have an object
// type.
+ Candidate.TookAddressOfOverload =
+ CandidateSet.getKind() ==
+ OverloadCandidateSet::CSK_AddressOfOverloadSet;
+
Candidate.IgnoreObjectArgument =
isa<CXXMethodDecl>(Candidate.Function) &&
+ cast<CXXMethodDecl>(Candidate.Function)
+ ->isImplicitObjectMemberFunction() &&
!isa<CXXConstructorDecl>(Candidate.Function);
+
Candidate.ExplicitCallArguments = Args.size();
if (Result == TemplateDeductionResult::NonDependentConversionFailure)
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -8093,9 +8112,12 @@ bool Sema::CheckNonDependentConversions(
// that is correct.
const bool AllowExplicit = false;
+ bool ForOverloadSetAddressResolution =
+ CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet;
auto *FD = FunctionTemplate->getTemplatedDecl();
auto *Method = dyn_cast<CXXMethodDecl>(FD);
- bool HasThisConversion = Method && !isa<CXXConstructorDecl>(Method);
+ bool HasThisConversion = !ForOverloadSetAddressResolution && Method &&
+ !isa<CXXConstructorDecl>(Method);
unsigned ThisConversions = HasThisConversion ? 1 : 0;
if (Conversions.empty())
@@ -8165,7 +8187,8 @@ bool Sema::CheckNonDependentConversions(
};
unsigned Offset =
- Method && Method->hasCXXExplicitFunctionObjectParameter() ? 1 : 0;
+ HasThisConversion && Method->hasCXXExplicitFunctionObjectParameter() ? 1
+ : 0;
for (unsigned I = 0, N = std::min(ParamTypes.size() - Offset, Args.size());
I != N; ++I) {
@@ -10780,7 +10803,8 @@ bool clang::isBetterOverloadCandidate(
// any function G, and, symmetrically, ICS1(G) is neither
// better nor worse than ICS1(F).
unsigned StartArg = 0;
- if (Cand1.IgnoreObjectArgument || Cand2.IgnoreObjectArgument)
+ if (!Cand1.TookAddressOfOverload &&
+ (Cand1.IgnoreObjectArgument || Cand2.IgnoreObjectArgument))
StartArg = 1;
auto IsIllFormedConversion = [&](const ImplicitConversionSequence &ICS) {
@@ -11797,7 +11821,8 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand,
// non-constructor method. Note that 'I' corresponds the
// conversion-slot index.
bool isObjectArgument = false;
- if (isa<CXXMethodDecl>(Fn) && !isa<CXXConstructorDecl>(Fn)) {
+ if (!TakingCandidateAddress && isa<CXXMethodDecl>(Fn) &&
+ !isa<CXXConstructorDecl>(Fn)) {
if (I == 0)
isObjectArgument = true;
else if (!Fn->hasCXXExplicitFunctionObjectParameter())
@@ -12296,7 +12321,7 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
}
case TemplateDeductionResult::TooManyArguments:
case TemplateDeductionResult::TooFewArguments:
- DiagnoseArityMismatch(S, Found, Templated, NumArgs);
+ DiagnoseArityMismatch(S, Found, Templated, NumArgs, TakingCandidateAddress);
return;
case TemplateDeductionResult::InstantiationDepth:
@@ -13073,8 +13098,10 @@ CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
// Attempt to fix the bad conversion.
unsigned ConvCount = Cand->Conversions.size();
- for (unsigned ConvIdx = (Cand->IgnoreObjectArgument ? 1 : 0); /**/;
- ++ConvIdx) {
+ for (unsigned ConvIdx =
+ ((!Cand->TookAddressOfOverload && Cand->IgnoreObjectArgument) ? 1
+ : 0);
+ /**/; ++ConvIdx) {
assert(ConvIdx != ConvCount && "no bad conversion in candidate");
if (Cand->Conversions[ConvIdx].isInitialized() &&
Cand->Conversions[ConvIdx].isBad()) {
@@ -13259,7 +13286,7 @@ void OverloadCandidateSet::NoteCandidates(Sema &S, ArrayRef<Expr *> Args,
if (Cand->Function)
NoteFunctionCandidate(S, Cand, Args.size(),
- /*TakingCandidateAddress=*/false, DestAS);
+ Kind == CSK_AddressOfOverloadSet, DestAS);
else if (Cand->IsSurrogate)
NoteSurrogateCandidate(S, Cand);
else {
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 94edd553aacd7..d09a72b71b805 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3885,6 +3885,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
TemplateDeductionInfo &Info,
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs,
bool PartialOverloading, bool PartialOrdering,
+ bool ForOverloadSetAddressResolution,
llvm::function_ref<bool(bool)> CheckNonDependent) {
// Unevaluated SFINAE context.
EnterExpressionEvaluationContext Unevaluated(
@@ -4034,7 +4035,10 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
auto ParamIdx = OriginalArg.ArgIdx;
unsigned ExplicitOffset =
- Specialization->hasCXXExplicitFunctionObjectParameter() ? 1 : 0;
+ (Specialization->hasCXXExplicitFunctionObjectParameter() &&
+ !ForOverloadSetAddressResolution)
+ ? 1
+ : 0;
if (ParamIdx >= Specialization->getNumParams() - ExplicitOffset)
// FIXME: This presumably means a pack ended up smaller than we
// expected while deducing. Should this not result in deduction
@@ -4681,6 +4685,7 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
Result = FinishTemplateArgumentDeduction(
FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info,
&OriginalCallArgs, PartialOverloading, PartialOrdering,
+ ForOverloadSetAddressResolution,
[&, CallingCtx](bool OnlyInitializeNonUserDefinedConversions) {
ContextRAII SavedContext(*this, CallingCtx);
return CheckNonDependent(ParamTypesForArgChecking,
@@ -4797,7 +4802,7 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
Result = FinishTemplateArgumentDeduction(
FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info,
/*OriginalCallArgs=*/nullptr, /*PartialOverloading=*/false,
- /*PartialOrdering=*/true);
+ /*PartialOrdering=*/true, IsAddressOfFunction);
});
if (Result != TemplateDeductionResult::Success)
return Result;
@@ -4981,7 +4986,7 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
Result = FinishTemplateArgumentDeduction(
ConversionTemplate, Deduced, 0, ConversionSpecialized, Info,
&OriginalCallArgs, /*PartialOverloading=*/false,
- /*PartialOrdering=*/false);
+ /*PartialOrdering=*/false, /*ForOverloadSetAddressResolution*/ false);
});
Specialization = cast_or_null<CXXConversionDecl>(ConversionSpecialized);
return Result;
diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp
index 60d896443ecd1..426fc6cab66a8 100644
--- a/clang/test/CXX/drs/cwg26xx.cpp
+++ b/clang/test/CXX/drs/cwg26xx.cpp
@@ -345,14 +345,19 @@ void test() {
namespace cwg2692 { // cwg2692: 19
#if __cplusplus >= 202302L
- struct A {
+struct A {
static void f(A); // #cwg2692-1
void f(this A); // #cwg2692-2
- void g();
- };
+ template <typename T>
+ static void g(T); // #cwg2692-3
+ template <typename T>
+ void g(this T); // #cwg2692-4
+
+ void test();
+};
- void A::g() {
+void A::test() {
(&A::f)(A());
// since-cxx23-error at -1 {{call to 'f' is ambiguous}}
// since-cxx23-note@#cwg2692-1 {{candidate function}}
@@ -361,6 +366,16 @@ namespace cwg2692 { // cwg2692: 19
// since-cxx23-error at -1 {{no matching function for call to 'f'}}
// since-cxx23-note@#cwg2692-1 {{candidate function not viable: requires 1 argument, but 0 were provided}}
// since-cxx23-note@#cwg2692-2 {{candidate function not viable: requires 1 argument, but 0 were provided}}
- }
+
+
+ (&A::g)(A());
+ // since-cxx23-error at -1 {{call to 'g' is ambiguous}}
+ // since-cxx23-note@#cwg2692-3 {{candidate function}}
+ // since-cxx23-note@#cwg2692-4 {{candidate function}}
+ (&A::g<A>)();
+ // since-cxx23-error at -1 {{no matching function for call to 'g'}}
+ // since-cxx23-note@#cwg2692-3 {{candidate function template not viable: requires 1 argument, but 0 were provided}}
+ // since-cxx23-note@#cwg2692-4 {{candidate function [with T = cwg2692::A] not viable: requires 1 argument, but 0 were provided}}
+}
#endif
} // namespace cwg2692
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index 2286da8d1c0e5..3a3dc8855d827 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -1168,3 +1168,125 @@ struct S {
bool g() { return f(); } // expected-error {{no viable conversion from returned value of type 'S' to function return type 'bool'}}
};
}
+
+namespace tpl_address {
+
+struct A {
+ template <typename T>
+ void a(this T self); // #tpl-address-a
+
+ template <typename T>
+ void b(this T&& self); // #tpl-address-b
+
+ template <typename T>
+ void c(this T self, int); // #tpl-address-c
+
+ template <typename T, typename U>
+ void d(this T self, U); // #tpl-address-d
+
+ template <typename T, typename U>
+ requires __is_same_as(U, int) void e(this T self, U); // #tpl-address-e
+
+ template <typename T>
+ requires __is_same_as(T, int) void f(this T self); // #tpl-address-f
+
+ template <typename T>
+ void g(this T self); // #tpl-address-g1
+
+ template <typename T>
+ void g(this T self, int); // #tpl-address-g2
+
+};
+
+void f() {
+ A a{};
+
+ (&A::a<A>)(a);
+
+ (&A::a)(a);
+
+ (&A::a<A>)();
+ // expected-error at -1 {{no matching function for call to 'a'}} \
+ // expected-note@#tpl-address-a {{candidate function [with T = tpl_address::A] not viable: requires 1 argument, but 0 were provided}}
+
+ (&A::a)();
+ // expected-error at -1 {{no matching function for call to 'a'}} \
+ // expected-note@#tpl-address-a {{candidate template ignored: couldn't infer template argument 'T'}}
+
+ (&A::a<A>)(0);
+ // expected-error at -1 {{no matching function for call to 'a'}} \
+ // expected-note@#tpl-address-a {{candidate function template not viable: no known conversion from 'int' to 'A' for 1st argument}}
+
+ (&A::a<A>)(a, 1);
+ // expected-error at -1 {{no matching function for call to 'a'}} \
+ // expected-note@#tpl-address-a {{candidate function template not viable: requires 1 argument, but 2 were provided}}
+
+
+ (&A::b<A>)(a);
+ // expected-error at -1 {{no matching function for call to 'b'}} \
+ // expected-note@#tpl-address-b{{candidate function template not viable: expects an rvalue for 1st argument}}
+
+ (&A::b)(a);
+
+ (&A::c<A>)(a, 0);
+
+ (&A::c<A>)(a);
+ // expected-error at -1 {{no matching function for call to 'c'}} \
+ // expected-note@#tpl-address-c{{candidate function [with T = tpl_address::A] not viable: requires 2 arguments, but 1 was provided}}
+
+ (&A::c<A>)(a, 0, 0);
+ // expected-error at -1 {{no matching function for call to 'c'}} \
+ // expected-note@#tpl-address-c{{candidate function template not viable: requires 2 arguments, but 3 were provided}}
+
+ (&A::c<A>)(a, a);
+ // expected-error at -1 {{no matching function for call to 'c'}} \
+ // expected-note@#tpl-address-c{{candidate function template not viable: no known conversion from 'A' to 'int' for 2nd argument}}
+
+ (&A::d)(a, 0);
+ (&A::d)(a, a);
+ (&A::d<A>)(a, 0);
+ (&A::d<A>)(a, a);
+ (&A::d<A, int>)(a, 0);
+
+ (&A::d<A, int>)(a, a);
+ // expected-error at -1 {{no matching function for call to 'd'}} \
+ // expected-note@#tpl-address-d{{no known conversion from 'A' to 'int' for 2nd argument}}
+
+
+ (&A::e)(a, 0);
+ (&A::e)(a, a);
+ // expected-error at -1 {{no matching function for call to 'e'}} \
+ // expected-note@#tpl-address-e{{candidate template ignored: constraints not satisfied [with T = A, U = A]}} \
+ // expected-note@#tpl-address-e{{because '__is_same(tpl_address::A, int)' evaluated to false}}
+
+ (&A::e<A>)(a, 0);
+ (&A::e<A>)(a, a);
+ // expected-error at -1 {{no matching function for call to 'e'}} \
+ // expected-note@#tpl-address-e{{candidate template ignored: constraints not satisfied [with T = A, U = A]}} \
+ // expected-note@#tpl-address-e{{because '__is_same(tpl_address::A, int)' evaluated to false}}
+
+ (&A::e<A, int>)(a, 0);
+
+ (&A::f<int>)(0);
+ (&A::f)(0);
+
+ (&A::f<A>)(a);
+ // expected-error at -1 {{no matching function for call to 'f'}} \
+ // expected-note@#tpl-address-f{{candidate template ignored: constraints not satisfied [with T = A]}} \
+ // expected-note@#tpl-address-f{{because '__is_same(tpl_address::A, int)' evaluated to false}}
+
+ (&A::f)(a);
+ // expected-error at -1 {{no matching function for call to 'f'}} \
+ // expected-note@#tpl-address-f{{candidate template ignored: constraints not satisfied [with T = A]}} \
+ // expected-note@#tpl-address-f{{because '__is_same(tpl_address::A, int)' evaluated to false}}
+
+ (&A::g)(a);
+ (&A::g)(a, 0);
+ (&A::g)(a, 0, 0);
+ // expected-error at -1 {{no matching function for call to 'g'}} \
+ // expected-note@#tpl-address-g2 {{candidate function template not viable: requires 2 arguments, but 3 were provided}}\
+ // expected-note@#tpl-address-g1 {{candidate function template not viable: requires 1 argument, but 3 were provided}}
+}
+
+
+}
>From 3c9d307abe6e65be7c336172644f059cac701269 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 7 Jul 2025 17:17:11 +0200
Subject: [PATCH 2/2] Aaron's feedback
---
clang/lib/Sema/SemaOverload.cpp | 52 ++++++++++++++++-----------------
1 file changed, 26 insertions(+), 26 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index f72da2b59f42e..7af3acacb5ba6 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7762,31 +7762,31 @@ void Sema::AddMethodCandidate(
Candidate.Viable = true;
unsigned FirstConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0;
- if (IgnoreExplicitObject)
- ;
- else if (ObjectType.isNull())
- Candidate.IgnoreObjectArgument = true;
- else if (Method->isStatic()) {
- // [over.best.ics.general]p8
- // When the parameter is the implicit object parameter of a static member
- // function, the implicit conversion sequence is a standard conversion
- // sequence that is neither better nor worse than any other standard
- // conversion sequence.
- //
- // This is a rule that was introduced in C++23 to support static lambdas. We
- // apply it retroactively because we want to support static lambdas as an
- // extension and it doesn't hurt previous code.
- Candidate.Conversions[FirstConvIdx].setStaticObjectArgument();
- } else {
- // Determine the implicit conversion sequence for the object
- // parameter.
- Candidate.Conversions[FirstConvIdx] = TryObjectArgumentInitialization(
- *this, CandidateSet.getLocation(), ObjectType, ObjectClassification,
- Method, ActingContext, /*InOverloadResolution=*/true);
- if (Candidate.Conversions[FirstConvIdx].isBad()) {
- Candidate.Viable = false;
- Candidate.FailureKind = ovl_fail_bad_conversion;
- return;
+ if (!IgnoreExplicitObject) {
+ if (ObjectType.isNull())
+ Candidate.IgnoreObjectArgument = true;
+ else if (Method->isStatic()) {
+ // [over.best.ics.general]p8
+ // When the parameter is the implicit object parameter of a static member
+ // function, the implicit conversion sequence is a standard conversion
+ // sequence that is neither better nor worse than any other standard
+ // conversion sequence.
+ //
+ // This is a rule that was introduced in C++23 to support static lambdas.
+ // We apply it retroactively because we want to support static lambdas as
+ // an extension and it doesn't hurt previous code.
+ Candidate.Conversions[FirstConvIdx].setStaticObjectArgument();
+ } else {
+ // Determine the implicit conversion sequence for the object
+ // parameter.
+ Candidate.Conversions[FirstConvIdx] = TryObjectArgumentInitialization(
+ *this, CandidateSet.getLocation(), ObjectType, ObjectClassification,
+ Method, ActingContext, /*InOverloadResolution=*/true);
+ if (Candidate.Conversions[FirstConvIdx].isBad()) {
+ Candidate.Viable = false;
+ Candidate.FailureKind = ovl_fail_bad_conversion;
+ return;
+ }
}
}
@@ -7883,7 +7883,7 @@ static void AddMethodTemplateCandidateImmediately(
// function template are combined with the set of non-template candidate
// functions.
TemplateDeductionInfo Info(CandidateSet.getLocation());
- auto Method = cast<CXXMethodDecl>(MethodTmpl->getTemplatedDecl());
+ auto *Method = cast<CXXMethodDecl>(MethodTmpl->getTemplatedDecl());
FunctionDecl *Specialization = nullptr;
ConversionSequenceList Conversions;
if (TemplateDeductionResult Result = S.DeduceTemplateArguments(
More information about the cfe-commits
mailing list