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)()``). 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 const *OriginalCallArgs, bool PartialOverloading, bool PartialOrdering, + bool ForOverloadSetAddressResolution, llvm::function_ref CheckNonDependent = [](bool /*OnlyInitializeNonUserDefinedConversions*/) { return false; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 34f512b13d6a8..7af3acacb5ba6 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,29 +7762,31 @@ void Sema::AddMethodCandidate( Candidate.Viable = true; unsigned FirstConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0; - 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; + } } } @@ -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(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(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(Candidate.Function) && + cast(Candidate.Function) + ->isImplicitObjectMemberFunction() && !isa(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(FD); - bool HasThisConversion = Method && !isa(Method); + bool HasThisConversion = !ForOverloadSetAddressResolution && Method && + !isa(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(Fn) && !isa(Fn)) { + if (!TakingCandidateAddress && isa(Fn) && + !isa(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 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 const *OriginalCallArgs, bool PartialOverloading, bool PartialOrdering, + bool ForOverloadSetAddressResolution, llvm::function_ref 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(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 + static void g(T); // #cwg2692-3 + template + void g(this T); // #cwg2692-4 + + void test(); +}; - void A::g() { +void A::test() { (&A::f)(A()); // since-cxx23-error@-1 {{call to 'f' is ambiguous}} // since-cxx23-note@#cwg2692-1 {{candidate function}} @@ -361,6 +366,16 @@ namespace cwg2692 { // cwg2692: 19 // since-cxx23-error@-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@-1 {{call to 'g' is ambiguous}} + // since-cxx23-note@#cwg2692-3 {{candidate function}} + // since-cxx23-note@#cwg2692-4 {{candidate function}} + (&A::g)(); + // since-cxx23-error@-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 + void a(this T self); // #tpl-address-a + + template + void b(this T&& self); // #tpl-address-b + + template + void c(this T self, int); // #tpl-address-c + + template + void d(this T self, U); // #tpl-address-d + + template + requires __is_same_as(U, int) void e(this T self, U); // #tpl-address-e + + template + requires __is_same_as(T, int) void f(this T self); // #tpl-address-f + + template + void g(this T self); // #tpl-address-g1 + + template + void g(this T self, int); // #tpl-address-g2 + +}; + +void f() { + A a{}; + + (&A::a)(a); + + (&A::a)(a); + + (&A::a)(); + // expected-error@-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@-1 {{no matching function for call to 'a'}} \ + // expected-note@#tpl-address-a {{candidate template ignored: couldn't infer template argument 'T'}} + + (&A::a)(0); + // expected-error@-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, 1); + // expected-error@-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); + // expected-error@-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, 0); + + (&A::c)(a); + // expected-error@-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, 0, 0); + // expected-error@-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); + // expected-error@-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, 0); + (&A::d)(a, a); + (&A::d)(a, 0); + + (&A::d)(a, a); + // expected-error@-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@-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, 0); + (&A::e)(a, a); + // expected-error@-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, 0); + + (&A::f)(0); + (&A::f)(0); + + (&A::f)(a); + // expected-error@-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@-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@-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}} +} + + +}