[clang] b9183d0 - [Clang][Sema] Ensure that the selected candidate for a member function explicit specialization is more constrained than all others (#101721)

via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 6 08:33:53 PDT 2024


Author: Krystian Stasiowski
Date: 2024-08-06T11:33:50-04:00
New Revision: b9183d0d0e24d164d3b57bf81ae911a22094e897

URL: https://github.com/llvm/llvm-project/commit/b9183d0d0e24d164d3b57bf81ae911a22094e897
DIFF: https://github.com/llvm/llvm-project/commit/b9183d0d0e24d164d3b57bf81ae911a22094e897.diff

LOG: [Clang][Sema] Ensure that the selected candidate for a member function explicit specialization is more constrained than all others (#101721)

The selection of the most constrained candidate for member function
explicit specializations introduced in #88963 does not check whether the
selected candidate is more constrained than all other candidates, which
can result in ambiguities being undiagnosed. This patch addresses the
issue.

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaTemplate.cpp
    clang/test/CXX/temp/temp.spec/temp.expl.spec/p14-23.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 68c3c3b685675b..d322da81723a5f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -190,6 +190,7 @@ Bug Fixes to C++ Support
 - Fix init-capture packs having a size of one before being instantiated. (#GH63677)
 - Clang now preserves the unexpanded flag in a lambda transform used for pack expansion. (#GH56852), (#GH85667),
   (#GH99877).
+- Fixed a bug when diagnosing ambiguous explicit specializations of constrained member functions.
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 1f4bfa247b544a..bab32b99b75bec 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7964,8 +7964,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(
     D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous));
   } else {
     // If this is an explicit specialization of a static data member, check it.
-    if (IsMemberSpecialization && !IsVariableTemplateSpecialization &&
-        !NewVD->isInvalidDecl() && CheckMemberSpecialization(NewVD, Previous))
+    if (IsMemberSpecialization && !IsVariableTemplate &&
+        !IsVariableTemplateSpecialization && !NewVD->isInvalidDecl() &&
+        CheckMemberSpecialization(NewVD, Previous))
       NewVD->setInvalidDecl();
 
     // Merge the decl with the existing one if appropriate.
@@ -10466,7 +10467,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
                                                 Previous))
           NewFD->setInvalidDecl();
       }
-    } else if (isMemberSpecialization && isa<CXXMethodDecl>(NewFD)) {
+    } else if (isMemberSpecialization && !FunctionTemplate) {
       if (CheckMemberSpecialization(NewFD, Previous))
           NewFD->setInvalidDecl();
     }

diff  --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 1346a4a3f0012a..6e2cb59b2b3dbc 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -9051,7 +9051,8 @@ bool Sema::CheckFunctionTemplateSpecialization(
 
 bool
 Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) {
-  assert(!isa<TemplateDecl>(Member) && "Only for non-template members");
+  assert(!Member->isTemplateDecl() && !Member->getDescribedTemplate() &&
+         "Only for non-template members");
 
   // Try to find the member we are instantiating.
   NamedDecl *FoundInstantiation = nullptr;
@@ -9062,21 +9063,25 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) {
   if (Previous.empty()) {
     // Nowhere to look anyway.
   } else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Member)) {
-    SmallVector<FunctionDecl *> Candidates;
-    bool Ambiguous = false;
-    for (LookupResult::iterator I = Previous.begin(), E = Previous.end();
-           I != E; ++I) {
-      CXXMethodDecl *Method =
-          dyn_cast<CXXMethodDecl>((*I)->getUnderlyingDecl());
+    UnresolvedSet<8> Candidates;
+    for (NamedDecl *Candidate : Previous) {
+      auto *Method = dyn_cast<CXXMethodDecl>(Candidate->getUnderlyingDecl());
+      // Ignore any candidates that aren't member functions.
       if (!Method)
         continue;
+
       QualType Adjusted = Function->getType();
       if (!hasExplicitCallingConv(Adjusted))
         Adjusted = adjustCCAndNoReturn(Adjusted, Method->getType());
+      // Ignore any candidates with the wrong type.
       // This doesn't handle deduced return types, but both function
       // declarations should be undeduced at this point.
+      // FIXME: The exception specification should probably be ignored when
+      // comparing the types.
       if (!Context.hasSameType(Adjusted, Method->getType()))
         continue;
+
+      // Ignore any candidates with unsatisfied constraints.
       if (ConstraintSatisfaction Satisfaction;
           Method->getTrailingRequiresClause() &&
           (CheckFunctionConstraints(Method, Satisfaction,
@@ -9084,29 +9089,53 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) {
                                     /*ForOverloadResolution=*/true) ||
            !Satisfaction.IsSatisfied))
         continue;
-      Candidates.push_back(Method);
-      FunctionDecl *MoreConstrained =
-          Instantiation ? getMoreConstrainedFunction(
-                              Method, cast<FunctionDecl>(Instantiation))
-                        : Method;
-      if (!MoreConstrained) {
-        Ambiguous = true;
-        continue;
+
+      Candidates.addDecl(Candidate);
+    }
+
+    // If we have no viable candidates left after filtering, we are done.
+    if (Candidates.empty())
+      return false;
+
+    // Find the function that is more constrained than every other function it
+    // has been compared to.
+    UnresolvedSetIterator Best = Candidates.begin();
+    CXXMethodDecl *BestMethod = nullptr;
+    for (UnresolvedSetIterator I = Candidates.begin(), E = Candidates.end();
+         I != E; ++I) {
+      auto *Method = cast<CXXMethodDecl>(I->getUnderlyingDecl());
+      if (I == Best ||
+          getMoreConstrainedFunction(Method, BestMethod) == Method) {
+        Best = I;
+        BestMethod = Method;
       }
-      if (MoreConstrained == Method) {
-        Ambiguous = false;
-        FoundInstantiation = *I;
-        Instantiation = Method;
-        InstantiatedFrom = Method->getInstantiatedFromMemberFunction();
-        MSInfo = Method->getMemberSpecializationInfo();
+    }
+
+    FoundInstantiation = *Best;
+    Instantiation = BestMethod;
+    InstantiatedFrom = BestMethod->getInstantiatedFromMemberFunction();
+    MSInfo = BestMethod->getMemberSpecializationInfo();
+
+    // Make sure the best candidate is more constrained than all of the others.
+    bool Ambiguous = false;
+    for (UnresolvedSetIterator I = Candidates.begin(), E = Candidates.end();
+         I != E; ++I) {
+      auto *Method = cast<CXXMethodDecl>(I->getUnderlyingDecl());
+      if (I != Best &&
+          getMoreConstrainedFunction(Method, BestMethod) != BestMethod) {
+        Ambiguous = true;
+        break;
       }
     }
+
     if (Ambiguous) {
       Diag(Member->getLocation(), diag::err_function_member_spec_ambiguous)
           << Member << (InstantiatedFrom ? InstantiatedFrom : Instantiation);
-      for (FunctionDecl *Candidate : Candidates)
+      for (NamedDecl *Candidate : Candidates) {
+        Candidate = Candidate->getUnderlyingDecl();
         Diag(Candidate->getLocation(), diag::note_function_member_spec_matched)
             << Candidate;
+      }
       return true;
     }
   } else if (isa<VarDecl>(Member)) {

diff  --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p14-23.cpp b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p14-23.cpp
index dc17cea99d4351..a023bf84137d78 100644
--- a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p14-23.cpp
+++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p14-23.cpp
@@ -1,60 +1,90 @@
 // RUN: %clang_cc1 -std=c++20 -verify %s
 
-template<int I>
-concept C = I >= 4;
+namespace N0 {
+  template<int I>
+  concept C = I >= 4;
 
-template<int I>
-concept D = I < 8;
+  template<int I>
+  concept D = I < 8;
 
-template<int I>
-struct A {
-  constexpr static int f() { return 0; }
-  constexpr static int f() requires C<I> && D<I> { return 1; }
-  constexpr static int f() requires C<I> { return 2; }
+  template<int I>
+  struct A {
+    constexpr static int f() { return 0; }
+    constexpr static int f() requires C<I> && D<I> { return 1; }
+    constexpr static int f() requires C<I> { return 2; }
 
-  constexpr static int g() requires C<I> { return 0; } // #candidate-0
-  constexpr static int g() requires D<I> { return 1; } // #candidate-1
+    constexpr static int g() requires C<I> { return 0; } // #candidate-0
+    constexpr static int g() requires D<I> { return 1; } // #candidate-1
 
-  constexpr static int h() requires C<I> { return 0; } // expected-note {{member declaration nearly matches}}
-};
+    constexpr static int h() requires C<I> { return 0; } // expected-note {{member declaration nearly matches}}
+  };
 
-template<>
-constexpr int A<2>::f() { return 3; }
+  template<>
+  constexpr int A<2>::f() { return 3; }
 
-template<>
-constexpr int A<4>::f() { return 4; }
+  template<>
+  constexpr int A<4>::f() { return 4; }
 
-template<>
-constexpr int A<8>::f() { return 5; }
+  template<>
+  constexpr int A<8>::f() { return 5; }
 
-static_assert(A<3>::f() == 0);
-static_assert(A<5>::f() == 1);
-static_assert(A<9>::f() == 2);
-static_assert(A<2>::f() == 3);
-static_assert(A<4>::f() == 4);
-static_assert(A<8>::f() == 5);
+  static_assert(A<3>::f() == 0);
+  static_assert(A<5>::f() == 1);
+  static_assert(A<9>::f() == 2);
+  static_assert(A<2>::f() == 3);
+  static_assert(A<4>::f() == 4);
+  static_assert(A<8>::f() == 5);
 
-template<>
-constexpr int A<0>::g() { return 2; }
+  template<>
+  constexpr int A<0>::g() { return 2; }
 
-template<>
-constexpr int A<8>::g() { return 3; }
+  template<>
+  constexpr int A<8>::g() { return 3; }
 
-template<>
-constexpr int A<6>::g() { return 4; } // expected-error {{ambiguous member function specialization 'A<6>::g' of 'A::g'}}
-                                      // expected-note@#candidate-0 {{member function specialization matches 'g'}}
-                                      // expected-note@#candidate-1 {{member function specialization matches 'g'}}
+  template<>
+  constexpr int A<6>::g() { return 4; } // expected-error {{ambiguous member function specialization 'N0::A<6>::g' of 'N0::A::g'}}
+                                        // expected-note@#candidate-0 {{member function specialization matches 'g'}}
+                                        // expected-note@#candidate-1 {{member function specialization matches 'g'}}
 
-static_assert(A<9>::g() == 0);
-static_assert(A<1>::g() == 1);
-static_assert(A<0>::g() == 2);
-static_assert(A<8>::g() == 3);
+  static_assert(A<9>::g() == 0);
+  static_assert(A<1>::g() == 1);
+  static_assert(A<0>::g() == 2);
+  static_assert(A<8>::g() == 3);
 
-template<>
-constexpr int A<4>::h() { return 1; }
+  template<>
+  constexpr int A<4>::h() { return 1; }
 
-template<>
-constexpr int A<0>::h() { return 2; } // expected-error {{out-of-line definition of 'h' does not match any declaration in 'A<0>'}}
+  template<>
+  constexpr int A<0>::h() { return 2; } // expected-error {{out-of-line definition of 'h' does not match any declaration in 'N0::A<0>'}}
 
-static_assert(A<5>::h() == 0);
-static_assert(A<4>::h() == 1);
+  static_assert(A<5>::h() == 0);
+  static_assert(A<4>::h() == 1);
+} // namespace N0
+
+namespace N1 {
+  template<int I>
+  concept C = I > 0;
+
+  template<int I>
+  concept D = I > 1;
+
+  template<int I>
+  concept E = I > 2;
+
+  template<int I>
+  struct A {
+    void f() requires C<I> && D<I>; // expected-note {{member function specialization matches 'f'}}
+    void f() requires C<I> && E<I>; // expected-note {{member function specialization matches 'f'}}
+    void f() requires C<I> && D<I> && true; // expected-note {{member function specialization matches 'f'}}
+
+    void g() requires C<I> && E<I>; // expected-note {{member function specialization matches 'g'}}
+    void g() requires C<I> && D<I>; // expected-note {{member function specialization matches 'g'}}
+    void g() requires C<I> && D<I> && true; // expected-note {{member function specialization matches 'g'}}
+  };
+
+  template<>
+  void A<3>::f(); // expected-error {{ambiguous member function specialization 'N1::A<3>::f' of 'N1::A::f'}}
+
+  template<>
+  void A<3>::g(); // expected-error {{ambiguous member function specialization 'N1::A<3>::g' of 'N1::A::g'}}
+} // namespace N1


        


More information about the cfe-commits mailing list