https://github.com/cor3ntin created https://github.com/llvm/llvm-project/pull/104438
Because there may be multiple constrained function of the same type, we need to perform overload resolution to find the best viable function to specialize. Fixes #46029 >From e3f210f4105d9eef5f34af893b5af81ac94e250b Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Thu, 15 Aug 2024 15:46:43 +0200 Subject: [PATCH] [Clang] Check constraints for an explicit instantiation of a member function Fixes #46029 Because there may be multiple constrained function of the same type, we need to perform overload resolution to find the best viable function to specialize. --- clang/docs/ReleaseNotes.rst | 3 +- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Sema/SemaTemplate.cpp | 64 +++++++++++++++---- .../explicit-instantiation-cxx20.cpp | 40 ++++++++++++ 4 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 clang/test/SemaTemplate/explicit-instantiation-cxx20.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index f5696d6ce15dc7..a7d49460b4bb5d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -73,7 +73,7 @@ C++ Specific Potentially Breaking Changes template <> // error: extraneous template head template <typename T> void f(); - + ABI Changes in This Version --------------------------- @@ -254,6 +254,7 @@ Bug Fixes to C++ Support specialization of a conversion function template. - Correctly diagnose attempts to use a concept name in its own definition; A concept name is introduced to its scope sooner to match the C++ standard. (#GH55875) +- Correctly check constraints of explicit instantiations of member functions. (#GH46029) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index da2f939067bfab..d5225783115892 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5663,6 +5663,8 @@ def err_explicit_instantiation_internal_linkage : Error< def err_explicit_instantiation_not_known : Error< "explicit instantiation of %0 does not refer to a function template, " "variable template, member function, member class, or static data member">; +def err_explicit_instantiation_no_candidate : Error< + "no candidate for explicit instantiation of %0">; def note_explicit_instantiation_here : Note< "explicit instantiation refers here">; def err_explicit_instantiation_data_member_not_instantiated : Error< diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 25585f683752ac..0afe6064bab185 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -10124,8 +10124,9 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, // instantiated from the member definition associated with its class // template. UnresolvedSet<8> TemplateMatches; - FunctionDecl *NonTemplateMatch = nullptr; - TemplateSpecCandidateSet FailedCandidates(D.getIdentifierLoc()); + OverloadCandidateSet NonTemplateMatches(D.getBeginLoc(), + OverloadCandidateSet::CSK_Normal); + TemplateSpecCandidateSet FailedTemplateCandidates(D.getIdentifierLoc()); for (LookupResult::iterator P = Previous.begin(), PEnd = Previous.end(); P != PEnd; ++P) { NamedDecl *Prev = *P; @@ -10137,9 +10138,18 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, if (Method->getPrimaryTemplate()) { TemplateMatches.addDecl(Method, P.getAccess()); } else { - // FIXME: Can this assert ever happen? Needs a test. - assert(!NonTemplateMatch && "Multiple NonTemplateMatches"); - NonTemplateMatch = Method; + OverloadCandidate &C = NonTemplateMatches.addCandidate(); + C.FoundDecl = P.getPair(); + C.Function = Method; + C.Viable = true; + ConstraintSatisfaction S; + if (Method->getTrailingRequiresClause() && + (CheckFunctionConstraints(Method, S, D.getIdentifierLoc(), + /*ForOverloadResolution=*/true) || + !S.IsSatisfied)) { + C.Viable = false; + C.FailureKind = ovl_fail_constraints_not_satisfied; + } } } } @@ -10149,16 +10159,16 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, if (!FunTmpl) continue; - TemplateDeductionInfo Info(FailedCandidates.getLocation()); + TemplateDeductionInfo Info(FailedTemplateCandidates.getLocation()); FunctionDecl *Specialization = nullptr; if (TemplateDeductionResult TDK = DeduceTemplateArguments( FunTmpl, (HasExplicitTemplateArgs ? &TemplateArgs : nullptr), R, Specialization, Info); TDK != TemplateDeductionResult::Success) { // Keep track of almost-matches. - FailedCandidates.addCandidate() - .set(P.getPair(), FunTmpl->getTemplatedDecl(), - MakeDeductionFailureInfo(Context, TDK, Info)); + FailedTemplateCandidates.addCandidate().set( + P.getPair(), FunTmpl->getTemplatedDecl(), + MakeDeductionFailureInfo(Context, TDK, Info)); (void)TDK; continue; } @@ -10172,7 +10182,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, CUDA().IdentifyTarget(Specialization, /* IgnoreImplicitHDAttr = */ true) != CUDA().IdentifyTarget(D.getDeclSpec().getAttributes())) { - FailedCandidates.addCandidate().set( + FailedTemplateCandidates.addCandidate().set( P.getPair(), FunTmpl->getTemplatedDecl(), MakeDeductionFailureInfo( Context, TemplateDeductionResult::CUDATargetMismatch, Info)); @@ -10182,12 +10192,40 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, TemplateMatches.addDecl(Specialization, P.getAccess()); } - FunctionDecl *Specialization = NonTemplateMatch; + FunctionDecl *Specialization = nullptr; + if (!NonTemplateMatches.empty()) { + unsigned Msg = 0; + OverloadCandidateDisplayKind DisplayKind; + OverloadCandidateSet::iterator Best; + switch (NonTemplateMatches.BestViableFunction(*this, D.getIdentifierLoc(), + Best)) { + case OR_Success: + case OR_Deleted: + Specialization = cast<FunctionDecl>(Best->Function); + break; + case OR_Ambiguous: + Msg = diag::err_explicit_instantiation_ambiguous; + DisplayKind = OCD_AmbiguousCandidates; + break; + case OR_No_Viable_Function: + Msg = diag::err_explicit_instantiation_no_candidate; + DisplayKind = OCD_AllCandidates; + break; + } + if (Msg) { + PartialDiagnostic Diag = PDiag(Msg) << Name; + NonTemplateMatches.NoteCandidates( + PartialDiagnosticAt(D.getIdentifierLoc(), Diag), *this, DisplayKind, + {}); + return true; + } + } + if (!Specialization) { // Find the most specialized function template specialization. UnresolvedSetIterator Result = getMostSpecialized( - TemplateMatches.begin(), TemplateMatches.end(), FailedCandidates, - D.getIdentifierLoc(), + TemplateMatches.begin(), TemplateMatches.end(), + FailedTemplateCandidates, D.getIdentifierLoc(), PDiag(diag::err_explicit_instantiation_not_known) << Name, PDiag(diag::err_explicit_instantiation_ambiguous) << Name, PDiag(diag::note_explicit_instantiation_candidate)); diff --git a/clang/test/SemaTemplate/explicit-instantiation-cxx20.cpp b/clang/test/SemaTemplate/explicit-instantiation-cxx20.cpp new file mode 100644 index 00000000000000..423aa3a39bd855 --- /dev/null +++ b/clang/test/SemaTemplate/explicit-instantiation-cxx20.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s + +template<class T> +concept C = true; + +template<class T> +concept D = C<T> && true; + +template<typename T> +struct a { + void no_candidate() requires(false) {} + // expected-note@-1 {{candidate function not viable: constraints not satisfied}} \ + // expected-note@-1 {{because 'false' evaluated to false}} + void no_candidate() requires(false && false) {} + // expected-note@-1 {{candidate function not viable: constraints not satisfied}} \ + // expected-note@-1 {{because 'false' evaluated to false}} + + void subsumes(); + void subsumes() requires C<T>; + void subsumes() requires D<T> {}; + + void ok() requires false; + void ok() requires true {}; + + void ok2() requires false; + void ok2(){}; + + void ambiguous() requires true; + // expected-note@-1 {{candidate function}} + void ambiguous() requires C<T>; + // expected-note@-1 {{candidate function}} +}; +template void a<int>::no_candidate(); +// expected-error@-1 {{no candidate for explicit instantiation of 'no_candidate'}} + +template void a<int>::ambiguous(); +// expected-error@-1 {{partial ordering for explicit instantiation of 'ambiguous' is ambiguous}} + +template void a<int>::ok(); +template void a<int>::ok2(); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits