https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/127773
>From 2d9c248c70b6d24f277982a32f36e2ef1bde2829 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Wed, 19 Feb 2025 17:13:56 +0800 Subject: [PATCH 1/4] [Clang] Implement CWG2918 'Consideration of constraints for address of overloaded function' --- clang/docs/ReleaseNotes.rst | 4 + clang/include/clang/Sema/Sema.h | 3 +- clang/lib/Sema/SemaConcept.cpp | 5 +- clang/lib/Sema/SemaOverload.cpp | 110 ++++++++++++++++++----- clang/lib/Sema/SemaTemplateDeduction.cpp | 9 +- clang/test/CXX/drs/cwg29xx.cpp | 84 +++++++++++++++++ clang/www/cxx_dr_status.html | 2 +- 7 files changed, 187 insertions(+), 30 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a91c764860ccd..b876b48c63717 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -88,6 +88,10 @@ Resolutions to C++ Defect Reports two releases. The improvements to template template parameter matching implemented in the previous release, as described in P3310 and P3579, made this flag unnecessary. +- Implemented `CWG2918 Consideration of constraints for address of overloaded ` + `function <https://cplusplus.github.io/CWG/issues/2918.html>`_ + + C Language Changes ------------------ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c55b964650323..3270e7afb3796 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10466,7 +10466,8 @@ class Sema final : public SemaBase { /// returned. FunctionDecl *ResolveSingleFunctionTemplateSpecialization( OverloadExpr *ovl, bool Complain = false, DeclAccessPair *Found = nullptr, - TemplateSpecCandidateSet *FailedTSC = nullptr); + TemplateSpecCandidateSet *FailedTSC = nullptr, + bool ForTypeDeduction = false); // Resolve and fix an overloaded expression that can be resolved // because it identifies a single function template specialization. diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 8a77cbf8c9477..f8bc176c1d8b5 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1773,6 +1773,7 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, NamedDecl *D2, MutableArrayRef<const Expr *> AC2, bool &Result) { +#ifndef NDEBUG if (const auto *FD1 = dyn_cast<FunctionDecl>(D1)) { auto IsExpectedEntity = [](const FunctionDecl *FD) { FunctionDecl::TemplatedKind Kind = FD->getTemplatedKind(); @@ -1780,13 +1781,11 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, Kind == FunctionDecl::TK_FunctionTemplate; }; const auto *FD2 = dyn_cast<FunctionDecl>(D2); - (void)IsExpectedEntity; - (void)FD1; - (void)FD2; assert(IsExpectedEntity(FD1) && FD2 && IsExpectedEntity(FD2) && "use non-instantiated function declaration for constraints partial " "ordering"); } +#endif if (AC1.empty()) { Result = AC2.empty(); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 8d5b5ac190b5b..77fc78dd8ce99 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10369,20 +10369,16 @@ static bool allowAmbiguity(ASTContext &Context, const FunctionDecl *F1, /// [over.match.best.general]p2.6 /// F1 and F2 are non-template functions with the same /// non-object-parameter-type-lists, and F1 is more constrained than F2 [...] -static bool sameFunctionParameterTypeLists(Sema &S, - const OverloadCandidate &Cand1, - const OverloadCandidate &Cand2) { - if (!Cand1.Function || !Cand2.Function) - return false; - - FunctionDecl *Fn1 = Cand1.Function; - FunctionDecl *Fn2 = Cand2.Function; - +static bool sameFunctionParameterTypeLists(Sema &S, FunctionDecl *Fn1, + FunctionDecl *Fn2, + bool IsFn1Reversed, + bool IsFn2Reversed) { + assert(Fn1 && Fn2); if (Fn1->isVariadic() != Fn2->isVariadic()) return false; - if (!S.FunctionNonObjectParamTypesAreEqual( - Fn1, Fn2, nullptr, Cand1.isReversed() ^ Cand2.isReversed())) + if (!S.FunctionNonObjectParamTypesAreEqual(Fn1, Fn2, nullptr, + IsFn1Reversed ^ IsFn2Reversed)) return false; auto *Mem1 = dyn_cast<CXXMethodDecl>(Fn1); @@ -10403,6 +10399,30 @@ static bool sameFunctionParameterTypeLists(Sema &S, return true; } +static FunctionDecl * +getMorePartialOrderingConstrained(Sema &S, FunctionDecl *Fn1, FunctionDecl *Fn2, + bool IsFn1Reversed, bool IsFn2Reversed) { + if (!Fn1 || !Fn2) + return nullptr; + + // C++ [temp.constr.order]: + // A non-template function F1 is more partial-ordering-constrained than a + // non-template function F2 if: + bool Cand1IsSpecialization = Fn1->getPrimaryTemplate(); + bool Cand2IsSpecialization = Fn2->getPrimaryTemplate(); + + if (Cand1IsSpecialization || Cand2IsSpecialization) + return nullptr; + + // - they have the same non-object-parameter-type-lists, and [...] + if (!sameFunctionParameterTypeLists(S, Fn1, Fn2, IsFn1Reversed, + IsFn2Reversed)) + return nullptr; + + // - the declaration of F1 is more constrained than the declaration of F2. + return S.getMoreConstrainedFunction(Fn1, Fn2); +} + /// isBetterOverloadCandidate - Determines whether the first overload /// candidate is a better candidate than the second (C++ 13.3.3p1). bool clang::isBetterOverloadCandidate( @@ -10649,12 +10669,12 @@ bool clang::isBetterOverloadCandidate( } } - // -— F1 and F2 are non-template functions with the same - // parameter-type-lists, and F1 is more constrained than F2 [...], - if (!Cand1IsSpecialization && !Cand2IsSpecialization && - sameFunctionParameterTypeLists(S, Cand1, Cand2) && - S.getMoreConstrainedFunction(Cand1.Function, Cand2.Function) == - Cand1.Function) + // -— F1 and F2 are non-template functions and F1 is more + // partial-ordering-constrained than F2 [...], + if (FunctionDecl *F = getMorePartialOrderingConstrained( + S, Cand1.Function, Cand2.Function, Cand1.isReversed(), + Cand2.isReversed()); + F && F == Cand1.Function) return true; // -- F1 is a constructor for a class D, F2 is a constructor for a base @@ -12999,9 +13019,10 @@ class AddressOfFunctionResolver { // C++ [over.over]p4: // If more than one function is selected, [...] if (Matches.size() > 1 && !eliminiateSuboptimalOverloadCandidates()) { - if (FoundNonTemplateFunction) + if (FoundNonTemplateFunction) { EliminateAllTemplateMatches(); - else + EliminateLessPartialOrderingConstrainedMatches(); + } else EliminateAllExceptMostSpecializedTemplate(); } } @@ -13252,6 +13273,33 @@ class AddressOfFunctionResolver { } } + void EliminateLessPartialOrderingConstrainedMatches() { + // C++ [over.over]p5: + // [...] Any given non-template function F0 is eliminated if the set + // contains a second non-template function that is more + // partial-ordering-constrained than F0. [...] + assert(Matches[0].second->getPrimaryTemplate() == nullptr && + "Call EliminateAllTemplateMatches() first"); + SmallVector<std::pair<DeclAccessPair, FunctionDecl *>, 4> Results; + Results.push_back(Matches[0]); + for (unsigned I = 1, N = Matches.size(); I < N; ++I) { + assert(Matches[I].second->getPrimaryTemplate() == nullptr); + FunctionDecl *F = getMorePartialOrderingConstrained( + S, Matches[I].second, Results[0].second, + /*IsFn1Reversed=*/false, + /*IsFn2Reversed=*/false); + if (!F) { + Results.push_back(Matches[I]); + continue; + } + if (F == Matches[I].second) { + Results.clear(); + Results.push_back(Matches[I]); + } + } + std::swap(Matches, Results); + } + void EliminateSuboptimalCudaMatches() { S.CUDA().EraseUnwantedMatches(S.getCurFunctionDecl(/*AllowLambda=*/true), Matches); @@ -13426,7 +13474,15 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) { } // FD has the same CUDA prefernece than Result. Continue check // constraints. - FunctionDecl *MoreConstrained = getMoreConstrainedFunction(FD, Result); + + // C++ [over.over]p5: + // [...] Any given non-template function F0 is eliminated if the set + // contains a second non-template function that is more + // partial-ordering-constrained than F0 [...] + FunctionDecl *MoreConstrained = + getMorePartialOrderingConstrained(*this, FD, Result, + /*IsFn1Reversed=*/false, + /*IsFn2Reversed=*/false); if (MoreConstrained != FD) { if (!MoreConstrained) { IsResultAmbiguous = true; @@ -13443,9 +13499,9 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) { return nullptr; if (Result) { - SmallVector<const Expr *, 1> ResultAC; // We skipped over some ambiguous declarations which might be ambiguous with // the selected result. + // FIXME: This for-loop is dead code. Remove it? for (FunctionDecl *Skipped : AmbiguousDecls) { // If skipped candidate has different CUDA preference than the result, // there is no ambiguity. Otherwise check whether they have different @@ -13489,7 +13545,7 @@ bool Sema::resolveAndFixAddressOfSingleOverloadCandidate( FunctionDecl *Sema::ResolveSingleFunctionTemplateSpecialization( OverloadExpr *ovl, bool Complain, DeclAccessPair *FoundResult, - TemplateSpecCandidateSet *FailedTSC) { + TemplateSpecCandidateSet *FailedTSC, bool ForTypeDeduction) { // C++ [over.over]p1: // [...] [Note: any redundant set of parentheses surrounding the // overloaded function name is ignored (5.1). ] @@ -13540,8 +13596,16 @@ FunctionDecl *Sema::ResolveSingleFunctionTemplateSpecialization( assert(Specialization && "no specialization and no error?"); - // Multiple matches; we can't resolve to a single declaration. + // C++ [temp.deduct.call]p6: + // [...] If all successful deductions yield the same deduced A, that + // deduced A is the result of deduction; otherwise, the parameter is + // treated as a non-deduced context. if (Matched) { + if (ForTypeDeduction && + isSameOrCompatibleFunctionType(Matched->getType(), + Specialization->getType())) + continue; + // Multiple matches; we can't resolve to a single declaration. if (Complain) { Diag(ovl->getExprLoc(), diag::err_addr_ovl_ambiguous) << ovl->getName(); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index eb70dd743c8be..627cd82ed1c77 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4252,7 +4252,8 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams, if (FunctionDecl *ExplicitSpec = S.ResolveSingleFunctionTemplateSpecialization( Ovl, /*Complain=*/false, - /*FoundDeclAccessPair=*/nullptr, FailedTSC)) + /*Found=*/nullptr, FailedTSC, + /*ForTypeDeduction=*/true)) return GetTypeOfFunction(S, R, ExplicitSpec); } @@ -4321,7 +4322,11 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams, /*HasDeducedAnyParam=*/nullptr); if (Result != TemplateDeductionResult::Success) continue; - if (!Match.isNull()) + // C++ [temp.deduct.call]p6: + // [...] If all successful deductions yield the same deduced A, that + // deduced A is the result of deduction; otherwise, the parameter is + // treated as a non-deduced context. [...] + if (!Match.isNull() && !S.isSameOrCompatibleFunctionType(Match, ArgType)) return {}; Match = ArgType; } diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp index aeb62b8d68710..61c3ecc04b895 100644 --- a/clang/test/CXX/drs/cwg29xx.cpp +++ b/clang/test/CXX/drs/cwg29xx.cpp @@ -60,6 +60,90 @@ struct S { #endif } // namespace cwg2917 +namespace cwg2918 { // cwg2918: 21 + +#if __cplusplus >= 202002L + +namespace Example1 { + +template<bool B> struct X { + void f(short) requires B; + void f(long); + template<typename> void g(short) requires B; + template<typename> void g(long); +}; + +void test() { + &X<true>::f; // since-cxx20-error {{reference to overloaded function could not be resolved}} + &X<true>::g<int>; // since-cxx20-error {{reference to overloaded function could not be resolved}} +} + +} // namespace Example1 + +namespace Example2 { + +template <bool B> struct X { + static constexpr int f(short) requires B { + return 42; + } + static constexpr int f(short) { + return 24; + } +}; + +template <typename T> +constexpr int f(T) { return 1; } + +template <typename T> + requires __is_same(T, int) +constexpr int f(T) { return 2; } + +void test() { + constexpr auto x = &X<true>::f; + static_assert(__is_same(decltype(x), int(*const)(short)), ""); + static_assert(x(0) == 42, ""); + + constexpr auto y = &X<false>::f; + static_assert(__is_same(decltype(x), int(*const)(short))); + static_assert(y(0) == 24, ""); + + constexpr auto z = &f<int>; + static_assert(__is_same(decltype(z), int(*const)(int))); + static_assert(z(0) == 2, ""); + + // C++ [temp.deduct.call]p6: + // If the argument is an overload set containing one or more function templates, + // the parameter is treated as a non-deduced context. + auto w = f; // since-cxx20-error {{variable 'w' with type 'auto' has incompatible initializer of type '<overloaded function type>'}} +} + +} // namespace Example2 + +namespace Example3 { + +template <typename T> void f(T &&, void (*)(T &&)); // #cwg2918_f + +void g(int &); // #1 +inline namespace A { +void g(short &&); // #2 +} +inline namespace B { +void g(short &&); // #3 +} + +void q() { + int x; + f(x, g); + // since-cxx20-error@-1 {{no matching function for call to 'f'}} + // since-cxx20-note@#cwg2918_f {{candidate template ignored: deduced conflicting types for parameter 'T' ('int &' vs. 'short')}} +} + +} // namespace Example3 + +#endif + +} // namespace cwg2918 + #if __cplusplus > 202302L namespace std { using size_t = decltype(sizeof(0)); diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 69ddd5e58b921..b674f8f094bb8 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -17364,7 +17364,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/2918.html">2918</a></td> <td>DR</td> <td>Consideration of constraints for address of overloaded function</td> - <td class="unknown" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 21</td> </tr> <tr id="2919"> <td><a href="https://cplusplus.github.io/CWG/issues/2919.html">2919</a></td> >From 5388856d4f693fa8bb0df6c5d73ce9be86907ed8 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Thu, 20 Feb 2025 20:20:57 +0800 Subject: [PATCH 2/4] Test Example3 in C++11+ --- clang/test/CXX/drs/cwg29xx.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp index 61c3ecc04b895..12dc966193e89 100644 --- a/clang/test/CXX/drs/cwg29xx.cpp +++ b/clang/test/CXX/drs/cwg29xx.cpp @@ -104,7 +104,7 @@ void test() { static_assert(x(0) == 42, ""); constexpr auto y = &X<false>::f; - static_assert(__is_same(decltype(x), int(*const)(short))); + static_assert(__is_same(decltype(y), int(*const)(short))); static_assert(y(0) == 24, ""); constexpr auto z = &f<int>; @@ -118,7 +118,9 @@ void test() { } } // namespace Example2 +#endif +#if __cplusplus >= 201103L namespace Example3 { template <typename T> void f(T &&, void (*)(T &&)); // #cwg2918_f @@ -134,8 +136,8 @@ void g(short &&); // #3 void q() { int x; f(x, g); - // since-cxx20-error@-1 {{no matching function for call to 'f'}} - // since-cxx20-note@#cwg2918_f {{candidate template ignored: deduced conflicting types for parameter 'T' ('int &' vs. 'short')}} + // since-cxx11-error@-1 {{no matching function for call to 'f'}} + // since-cxx11-note@#cwg2918_f {{candidate template ignored: deduced conflicting types for parameter 'T' ('int &' vs. 'short')}} } } // namespace Example3 >From 09ba784644ba1b72b48e11d3774c0ad84792c80d Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 21 Feb 2025 13:00:32 +0800 Subject: [PATCH 3/4] Remove unnecessary markers --- clang/test/CXX/drs/cwg29xx.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp index 12dc966193e89..f9c2e9ecf4618 100644 --- a/clang/test/CXX/drs/cwg29xx.cpp +++ b/clang/test/CXX/drs/cwg29xx.cpp @@ -125,12 +125,12 @@ namespace Example3 { template <typename T> void f(T &&, void (*)(T &&)); // #cwg2918_f -void g(int &); // #1 +void g(int &); inline namespace A { -void g(short &&); // #2 +void g(short &&); } inline namespace B { -void g(short &&); // #3 +void g(short &&); } void q() { >From e2ef79e15b873550519642c184ad3f405ba28329 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 21 Feb 2025 13:04:43 +0800 Subject: [PATCH 4/4] Remove an extra blank line --- clang/docs/ReleaseNotes.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b876b48c63717..e02325220bd9a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -91,7 +91,6 @@ Resolutions to C++ Defect Reports - Implemented `CWG2918 Consideration of constraints for address of overloaded ` `function <https://cplusplus.github.io/CWG/issues/2918.html>`_ - C Language Changes ------------------ _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits