Skip to content

[clang][Sema] Bugfix for choosing the more specialized overload #83279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 6, 2024

Conversation

HoBoIs
Copy link
Contributor

@HoBoIs HoBoIs commented Feb 28, 2024

There was a bug in Clang where it couldn't choose which overload candidate was more specialized if it was comparing a member-function to a non-member function. Previously, this was detected as an ambiguity, now Clang chooses correctly.

This patch fixes the bug by fully implementing CWG2445 and moving the template transformation described in [temp.func.order] paragraph 3 from isAtLeastAsSpecializedAs() to Sema::getMoreSpecializedTemplate() so we have the transformed parameter list during the whole comparison. Also, to be able to add the correct type for the implicit object parameter Sema::getMoreSpecializedTemplate() has new parameters for the object type.

Fixes #74494, fixes #82509

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Feb 28, 2024
@HoBoIs HoBoIs changed the title Bugfix for choosing the more specialized overload [clang] Bugfix for choosing the more specialized overload Feb 28, 2024
@llvmbot
Copy link
Member

llvmbot commented Feb 28, 2024

@llvm/pr-subscribers-clang

Author: Botond István Horváth (HoBoIs)

Changes

There was a bug in clang where it couldn't choose which overload candidate is more specialized if it was comparing a member-function to a non-member function. Previously, this was detected as an ambigouity, now clang chooses correctly.

This patch fixes the bug by fully implementing CWG2445 and moving the template transformation described in [temp.func.order] paragraph 3 from isAtLeastAsSpecializedAs to Sema::getMoreSpecializedTemplate so we have the transformed parameter list during the whole comperrassion. Also, to be able to add the correct type for the implicit object parameter Sema::getMoreSpecializedTemplate has new parameters for the object type.

Fixes #74494 and #82509


Full diff: https://github.com/llvm/llvm-project/pull/83279.diff

3 Files Affected:

  • (modified) clang/include/clang/Sema/Sema.h (+3-1)
  • (modified) clang/lib/Sema/SemaOverload.cpp (+11-1)
  • (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+172-91)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ef4b93fac95ce5..1a2a3a6bebd95e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9463,7 +9463,9 @@ class Sema final {
   FunctionTemplateDecl *getMoreSpecializedTemplate(
       FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
       TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-      unsigned NumCallArguments2, bool Reversed = false);
+      unsigned NumCallArguments2, QualType ObjType1 = {},
+      QualType ObjType2 = {}, bool Reversed = false);
+
   UnresolvedSetIterator
   getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
                      TemplateSpecCandidateSet &FailedCandidates,
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 7d38043890ca20..60138236abf1d8 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10526,14 +10526,24 @@ bool clang::isBetterOverloadCandidate(
   //      according to the partial ordering rules described in 14.5.5.2, or,
   //      if not that,
   if (Cand1IsSpecialization && Cand2IsSpecialization) {
+    const auto *ObjContext1 =
+        dyn_cast<CXXRecordDecl>(Cand1.FoundDecl->getDeclContext());
+    const auto *ObjContext2 =
+        dyn_cast<CXXRecordDecl>(Cand2.FoundDecl->getDeclContext());
     if (FunctionTemplateDecl *BetterTemplate = S.getMoreSpecializedTemplate(
             Cand1.Function->getPrimaryTemplate(),
             Cand2.Function->getPrimaryTemplate(), Loc,
             isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
                                                    : TPOC_Call,
             Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
-            Cand1.isReversed() ^ Cand2.isReversed()))
+            ObjContext1 ? QualType(ObjContext1->getTypeForDecl(), 0)
+                        : QualType{},
+            ObjContext2 ? QualType(ObjContext2->getTypeForDecl(), 0)
+                        : QualType{},
+            Cand1.isReversed() ^ Cand2.isReversed())) {
       return BetterTemplate == Cand1.Function->getPrimaryTemplate();
+    }
+
   }
 
   //   -— F1 and F2 are non-template functions with the same
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 563491f76f5478..2af3c29ae1f1c4 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5333,11 +5333,31 @@ bool Sema::CheckIfFunctionSpecializationIsImmediate(FunctionDecl *FD,
   return false;
 }
 
+static QualType GetImplicitObjectParameterTypeCXX20(ASTContext &Context,
+                                                    const CXXMethodDecl *Method,
+                                                    QualType rawType,
+                                                    bool isOtherRvr) {
+  // C++20 [temp.func.order]p3.1, p3.2:
+  //- The type X(M ) is “rvalue reference to cv A” if the optional ref-qualifier
+  //  of M is && or if M has no ref-qualifier and the positionally-corresponding
+  //  parameter of the other transformed template has rvalue reference type;
+  //  if this determination depends recursively upon whether X(M ) is an rvalue
+  //  reference type, it is not considered to have rvalue reference type.
+  //- Otherwise, X(M ) is “lvalue reference to cv A”.
+  assert(Method && !Method->isExplicitObjectMemberFunction() &&
+         "expected a member function with no explicit object parameter");
+
+  rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers());
+  if (Method->getRefQualifier() == RQ_RValue ||
+      (isOtherRvr && Method->getRefQualifier() == RQ_None))
+    return Context.getRValueReferenceType(rawType);
+  return Context.getLValueReferenceType(rawType);
+}
+
 /// If this is a non-static member function,
-static void
-AddImplicitObjectParameterType(ASTContext &Context,
-                               CXXMethodDecl *Method,
-                               SmallVectorImpl<QualType> &ArgTypes) {
+static QualType GetImplicitObjectParameterType(ASTContext &Context,
+                                               const CXXMethodDecl *Method,
+                                               QualType rawType) {
   // C++11 [temp.func.order]p3:
   //   [...] The new parameter is of type "reference to cv A," where cv are
   //   the cv-qualifiers of the function template (if any) and A is
@@ -5347,24 +5367,21 @@ AddImplicitObjectParameterType(ASTContext &Context,
   // reference type based on [over.match.funcs]p4.
   assert(Method && Method->isImplicitObjectMemberFunction() &&
          "expected an implicit objet function");
-  QualType ArgTy = Context.getTypeDeclType(Method->getParent());
-  ArgTy = Context.getQualifiedType(ArgTy, Method->getMethodQualifiers());
+  rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers());
   if (Method->getRefQualifier() == RQ_RValue)
-    ArgTy = Context.getRValueReferenceType(ArgTy);
-  else
-    ArgTy = Context.getLValueReferenceType(ArgTy);
-  ArgTypes.push_back(ArgTy);
+    return Context.getRValueReferenceType(rawType);
+  return Context.getLValueReferenceType(rawType);
 }
 
 /// Determine whether the function template \p FT1 is at least as
 /// specialized as \p FT2.
-static bool isAtLeastAsSpecializedAs(Sema &S,
-                                     SourceLocation Loc,
-                                     FunctionTemplateDecl *FT1,
-                                     FunctionTemplateDecl *FT2,
+static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
+                                     const FunctionTemplateDecl *FT1,
+                                     const FunctionTemplateDecl *FT2,
                                      TemplatePartialOrderingContext TPOC,
-                                     unsigned NumCallArguments1,
-                                     bool Reversed) {
+                                     bool Reversed,
+                                     const SmallVector<QualType, 4> &Args1,
+                                     const SmallVector<QualType, 4> &Args2) {
   assert(!Reversed || TPOC == TPOC_Call);
 
   FunctionDecl *FD1 = FT1->getTemplatedDecl();
@@ -5381,66 +5398,8 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
   //   The types used to determine the ordering depend on the context in which
   //   the partial ordering is done:
   TemplateDeductionInfo Info(Loc);
-  SmallVector<QualType, 4> Args2;
   switch (TPOC) {
-  case TPOC_Call: {
-    //   - In the context of a function call, the function parameter types are
-    //     used.
-    CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1);
-    CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2);
-
-    // C++11 [temp.func.order]p3:
-    //   [...] If only one of the function templates is a non-static
-    //   member, that function template is considered to have a new
-    //   first parameter inserted in its function parameter list. The
-    //   new parameter is of type "reference to cv A," where cv are
-    //   the cv-qualifiers of the function template (if any) and A is
-    //   the class of which the function template is a member.
-    //
-    // Note that we interpret this to mean "if one of the function
-    // templates is a non-static member and the other is a non-member";
-    // otherwise, the ordering rules for static functions against non-static
-    // functions don't make any sense.
-    //
-    // C++98/03 doesn't have this provision but we've extended DR532 to cover
-    // it as wording was broken prior to it.
-    SmallVector<QualType, 4> Args1;
-
-    unsigned NumComparedArguments = NumCallArguments1;
-
-    if (!Method2 && Method1 && Method1->isImplicitObjectMemberFunction()) {
-      // Compare 'this' from Method1 against first parameter from Method2.
-      AddImplicitObjectParameterType(S.Context, Method1, Args1);
-      ++NumComparedArguments;
-    } else if (!Method1 && Method2 &&
-               Method2->isImplicitObjectMemberFunction()) {
-      // Compare 'this' from Method2 against first parameter from Method1.
-      AddImplicitObjectParameterType(S.Context, Method2, Args2);
-    } else if (Method1 && Method2 && Reversed &&
-               Method1->isImplicitObjectMemberFunction() &&
-               Method2->isImplicitObjectMemberFunction()) {
-      // Compare 'this' from Method1 against second parameter from Method2
-      // and 'this' from Method2 against second parameter from Method1.
-      AddImplicitObjectParameterType(S.Context, Method1, Args1);
-      AddImplicitObjectParameterType(S.Context, Method2, Args2);
-      ++NumComparedArguments;
-    }
-
-    Args1.insert(Args1.end(), Proto1->param_type_begin(),
-                 Proto1->param_type_end());
-    Args2.insert(Args2.end(), Proto2->param_type_begin(),
-                 Proto2->param_type_end());
-
-    // C++ [temp.func.order]p5:
-    //   The presence of unused ellipsis and default arguments has no effect on
-    //   the partial ordering of function templates.
-    if (Args1.size() > NumComparedArguments)
-      Args1.resize(NumComparedArguments);
-    if (Args2.size() > NumComparedArguments)
-      Args2.resize(NumComparedArguments);
-    if (Reversed)
-      std::reverse(Args2.begin(), Args2.end());
-
+  case TPOC_Call:
     if (DeduceTemplateArguments(S, TemplateParams, Args2.data(), Args2.size(),
                                 Args1.data(), Args1.size(), Info, Deduced,
                                 TDF_None, /*PartialOrdering=*/true) !=
@@ -5448,7 +5407,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
       return false;
 
     break;
-  }
 
   case TPOC_Conversion:
     //   - In the context of a call to a conversion operator, the return types
@@ -5539,6 +5497,14 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
 /// \param NumCallArguments2 The number of arguments in the call to FT2, used
 /// only when \c TPOC is \c TPOC_Call.
 ///
+/// \param RawObjType1 The type of the object parameter of FT1 if a member
+/// function only used if \c TPOC is \c TPOC_Call and FT1 is a Function
+/// template from a member function
+///
+/// \param RawObjType2 The type of the object parameter of FT2 if a member
+/// function only used if \c TPOC is \c TPOC_Call and FT2 is a Function
+/// template from a member function
+///
 /// \param Reversed If \c true, exactly one of FT1 and FT2 is an overload
 /// candidate with a reversed parameter order. In this case, the corresponding
 /// P/A pairs between FT1 and FT2 are reversed.
@@ -5548,13 +5514,113 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
 FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
     FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
     TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-    unsigned NumCallArguments2, bool Reversed) {
+    unsigned NumCallArguments2, QualType RawObjType1, QualType RawObjType2,
+    bool Reversed) {
+  SmallVector<QualType, 4> Args1;
+  SmallVector<QualType, 4> Args2;
+  const FunctionDecl *FD1 = FT1->getTemplatedDecl();
+  const FunctionDecl *FD2 = FT2->getTemplatedDecl();
+  bool shouldConvert1 = false;
+  bool shouldConvert2 = false;
+  QualType ObjType1;
+  QualType ObjType2;
+  if (TPOC == TPOC_Call) {
+    const FunctionProtoType *Proto1 =
+        FD1->getType()->getAs<FunctionProtoType>();
+    const FunctionProtoType *Proto2 =
+        FD2->getType()->getAs<FunctionProtoType>();
 
-  bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC,
-                                          NumCallArguments1, Reversed);
-  bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC,
-                                          NumCallArguments2, Reversed);
+    //   - In the context of a function call, the function parameter types are
+    //     used.
+    const CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1);
+    const CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2);
+
+    if (getLangOpts().CPlusPlus20) {
+      // C++20 [temp.func.order]p3
+      //   [...] Each function template M that is a member function is
+      //   considered to have a new first parameter of type
+      //   X(M), described below, inserted in its function parameter list.
+      //
+      // Note that we interpret "that is a member function" as
+      // "that is a member function with no expicit object argument".
+      // Otherwise the ordering rules for methods with expicit objet arguments
+      // against anything else make no sense.
+      shouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction();
+      shouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction();
+      if (shouldConvert1) {
+        bool isR2 =
+            Method2 && !Method2->isExplicitObjectMemberFunction()
+                ? Method2->getRefQualifier() == RQ_RValue
+                : Proto2->param_type_begin()[0]->isRValueReferenceType();
+        // Compare 'this' from Method1 against first parameter from Method2.
+        ObjType1 = GetImplicitObjectParameterTypeCXX20(this->Context, Method1,
+                                                       RawObjType1, isR2);
+        Args1.push_back(ObjType1);
+      }
+      if (shouldConvert2) {
+        bool isR1 =
+            Method1 && !Method1->isExplicitObjectMemberFunction()
+                ? Method1->getRefQualifier() == RQ_RValue
+                : Proto1->param_type_begin()[0]->isRValueReferenceType();
+        // Compare 'this' from Method2 against first parameter from Method1.
+        ObjType2 = GetImplicitObjectParameterTypeCXX20(this->Context, Method2,
+                                                       RawObjType2, isR1);
+        Args2.push_back(ObjType2);
+      }
+    } else {
+      // C++11 [temp.func.order]p3:
+      //   [...] If only one of the function templates is a non-static
+      //   member, that function template is considered to have a new
+      //   first parameter inserted in its function parameter list. The
+      //   new parameter is of type "reference to cv A," where cv are
+      //   the cv-qualifiers of the function template (if any) and A is
+      //   the class of which the function template is a member.
+      //
+      // Note that we interpret this to mean "if one of the function
+      // templates is a non-static member and the other is a non-member";
+      // otherwise, the ordering rules for static functions against non-static
+      // functions don't make any sense.
+      //
+      // C++98/03 doesn't have this provision but we've extended DR532 to cover
+      // it as wording was broken prior to it.
+      shouldConvert1 =
+          !Method2 && Method1 && Method1->isImplicitObjectMemberFunction();
+      shouldConvert2 =
+          !Method1 && Method2 && Method2->isImplicitObjectMemberFunction();
+      if (shouldConvert1) {
+        // Compare 'this' from Method1 against first parameter from Method2.
+        ObjType1 =
+            GetImplicitObjectParameterType(this->Context, Method1, RawObjType1);
+        Args1.push_back(ObjType1);
+      }
+      if (shouldConvert2) {
+        // Compare 'this' from Method2 against first parameter from Method1.
+        ObjType2 =
+            GetImplicitObjectParameterType(this->Context, Method2, RawObjType2);
+        Args2.push_back(ObjType2);
+      }
+    }
+    unsigned NumComparedArguments = NumCallArguments1 + shouldConvert1;
+
+    Args1.insert(Args1.end(), Proto1->param_type_begin(),
+                 Proto1->param_type_end());
+    Args2.insert(Args2.end(), Proto2->param_type_begin(),
+                 Proto2->param_type_end());
 
+    // C++ [temp.func.order]p5:
+    //   The presence of unused ellipsis and default arguments has no effect on
+    //   the partial ordering of function templates.
+    if (Args1.size() > NumComparedArguments)
+      Args1.resize(NumComparedArguments);
+    if (Args2.size() > NumComparedArguments)
+      Args2.resize(NumComparedArguments);
+    if (Reversed)
+      std::reverse(Args2.begin(), Args2.end());
+  }
+  bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, Reversed,
+                                          Args1, Args2);
+  bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC, Reversed,
+                                          Args2, Args1);
   // C++ [temp.deduct.partial]p10:
   //   F is more specialized than G if F is at least as specialized as G and G
   //   is not at least as specialized as F.
@@ -5568,12 +5634,28 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
   //   ... and if G has a trailing function parameter pack for which F does not
   //   have a corresponding parameter, and if F does not have a trailing
   //   function parameter pack, then F is more specialized than G.
-  FunctionDecl *FD1 = FT1->getTemplatedDecl();
-  FunctionDecl *FD2 = FT2->getTemplatedDecl();
-  unsigned NumParams1 = FD1->getNumParams();
-  unsigned NumParams2 = FD2->getNumParams();
-  bool Variadic1 = NumParams1 && FD1->parameters().back()->isParameterPack();
-  bool Variadic2 = NumParams2 && FD2->parameters().back()->isParameterPack();
+
+  SmallVector<QualType> param1;
+  param1.reserve(FD1->param_size() + shouldConvert1);
+  if (shouldConvert1)
+    param1.push_back(ObjType1);
+  for (const auto &x : FD1->parameters())
+    param1.push_back(x->getType());
+
+  SmallVector<QualType> param2;
+  param2.reserve(FD2->param_size() + shouldConvert2);
+  if (shouldConvert2)
+    param2.push_back(ObjType2);
+  for (const auto &x : FD2->parameters())
+    param2.push_back(x->getType());
+
+  unsigned NumParams1 = param1.size();
+  unsigned NumParams2 = param2.size();
+
+  bool Variadic1 =
+      FD1->param_size() && FD1->parameters().back()->isParameterPack();
+  bool Variadic2 =
+      FD2->param_size() && FD2->parameters().back()->isParameterPack();
   if (Variadic1 != Variadic2) {
     if (Variadic1 && NumParams1 > NumParams2)
       return FT2;
@@ -5584,8 +5666,8 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
   // This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
   // there is no wording or even resolution for this issue.
   for (int i = 0, e = std::min(NumParams1, NumParams2); i < e; ++i) {
-    QualType T1 = FD1->getParamDecl(i)->getType().getCanonicalType();
-    QualType T2 = FD2->getParamDecl(i)->getType().getCanonicalType();
+    QualType T1 = param1[i].getCanonicalType();
+    QualType T2 = param2[i].getCanonicalType();
     auto *TST1 = dyn_cast<TemplateSpecializationType>(T1);
     auto *TST2 = dyn_cast<TemplateSpecializationType>(T2);
     if (!TST1 || !TST2)
@@ -5644,8 +5726,7 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
   // Any top-level cv-qualifiers modifying a parameter type are deleted when
   // forming the function type.
   for (unsigned i = 0; i < NumParams1; ++i)
-    if (!Context.hasSameUnqualifiedType(FD1->getParamDecl(i)->getType(),
-                                        FD2->getParamDecl(i)->getType()))
+    if (!Context.hasSameUnqualifiedType(param1[i], param2[i]))
       return nullptr;
 
   // C++20 [temp.func.order]p6.3:

@HoBoIs
Copy link
Contributor Author

HoBoIs commented Feb 28, 2024

@zygoloid @erichkeane Could you take a look?

Copy link

github-actions bot commented Feb 28, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs tests plus a release note.

Also, there is a lot of repeated code between the pre-c++20 and c++20 versions that will make this really hard to maintain, particularly with core issues, that I'd like to see merged better.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is close I think, just needs tests + ReleaseNotes.rst entry.

@HoBoIs HoBoIs requested a review from Endilll as a code owner March 1, 2024 13:40
Copy link
Contributor

@Endilll Endilll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DR changes looks good now, save for a nitpick.
However, I'm leaving it to other reviewers to check contents of the test. It looks like an example from the DR, but I don't feel qualified to say whether this test has sufficient coverage.

@HoBoIs
Copy link
Contributor Author

HoBoIs commented Mar 1, 2024

DR changes looks good now, save for a nitpick. However, I'm leaving it to other reviewers to check contents of the test. It looks like an example from the DR, but I don't feel qualified to say whether this test has sufficient coverage.

I have added tests to other files what are relevant for the DR too. Should I move them here?

@Endilll
Copy link
Contributor

Endilll commented Mar 1, 2024

I have added tests to other files what are relevant for the DR too. Should I move them here?

I trust your judgement here. Your PR touches a topic complicated enough that I don't feel qualified to make the judgement myself.

My stance is that DRs should be considered narrow in scope. So if you think that your additional tests are directly related to what the DR is about, go ahead. For instance, when DR is about cv-qualifiers in variable declarations, but the provided examples only cover const, I consider additional test for volatile to be directly related. On the other hand, if during implementation you realize that something is going on with cv-qualifiers of non-static member functions, and you want to test that, then it's a related issue, but not a part of the DR.

In my view, DR test suite prioritizes being on spot more than just containing sheer volume of tests.

@HoBoIs
Copy link
Contributor Author

HoBoIs commented Mar 4, 2024

I have added tests for rvalref-s and volatile-s.

@HoBoIs
Copy link
Contributor Author

HoBoIs commented Mar 5, 2024

@erichkeane a question: this PR is partly a fix for a DR for c++20. Right now I only apply the fix for C++20 and newer. Is there a reason for not applying this fix for older standards? I don't see how it would break any existing code, and making the fix retroactive doesn't make any test fail.

@cor3ntin
Copy link
Contributor

cor3ntin commented Mar 5, 2024

@erichkeane a question: this PR is partly a fix for a DR for c++20. Right now I only apply the fix for C++20 and newer. Is there a reason for not applying this fix for older standards? I don't see how it would break any existing code, and making the fix retroactive doesn't make any test fail.

I don't think checking for c++20 is useful because rewritten candidates are a c++20- feature anyway

@HoBoIs
Copy link
Contributor Author

HoBoIs commented Mar 5, 2024

I don't think checking for c++20 is useful because rewritten candidates are a c++20- feature anyway

Yes, but the resolution of the DR also talks about the reference type of the object parameter. (If I apply the patch retroactively the code will get simpler.)

@erichkeane
Copy link
Collaborator

Yeah, agree with @cor3ntin here. I don't think this needs the C++20 check. Additionally, DRs are usually backported as far as they are 'useful' when possible, so any checks in a DR are a bit of a 'smell' anyway.

@HoBoIs
Copy link
Contributor Author

HoBoIs commented Mar 5, 2024

@erichkeane Thank you for the suggestions, I have removed the checking for C++20 and I also changed to the Args.resize(std::min(...)) based on what you suggested. Is there anything else to be done?

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only some minor things left, so approving. Mostly, for the 'type' and 'context', just do them the way they would read:
So "Object One Type" (plus we use Ty to mean type). Same with Context.

Finally, SmallVector doesn't need a size set, so just using the 'default' value (which it calculates for you!) is sufficient here.

Feel free to merge when CI agrees with you on the above/below changes.

There was a bug in clang where it couldn't choose which overload candidate is
more specialized if it was comparing a member-function to a non-member
function. Previously, this was detected as an ambigouity, now clang chooses correctly.

This patch fixes the bug by fully implementing CWG2445 and moving the template
transformation described in [temp.func.order] paragraph 3 from
isAtLeastAsSpecializedAs to Sema::getMoreSpecializedTemplate so we have the
transformed parameter list during the whole comperrassion. Also, to be able
to add the correct type for the implicit object parameter
Sema::getMoreSpecializedTemplate has new parameters for the object type.
@whisperity whisperity changed the title [clang] Bugfix for choosing the more specialized overload [clang][Sema] Bugfix for choosing the more specialized overload Mar 6, 2024
Copy link
Member

@whisperity whisperity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Only style nits!)

@whisperity whisperity requested review from whisperity and removed request for whisperity March 6, 2024 14:00
Copy link
Member

@whisperity whisperity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@whisperity whisperity merged commit 63afcbb into llvm:main Mar 6, 2024
HoBoIs added a commit to HoBoIs/llvm-project that referenced this pull request Mar 11, 2024
…#83279)

There was a bug in Clang where it couldn't choose which overload
candidate was more specialized if it was comparing a member-function to
a non-member function. Previously, this was detected as an ambiguity,
now Clang chooses correctly.

This patch fixes the bug by fully implementing CWG2445 and moving the
template transformation described in `[temp.func.order]` paragraph 3
from `isAtLeastAsSpecializedAs()` to
`Sema::getMoreSpecializedTemplate()` so we have the transformed
parameter list during the whole comparison. Also, to be able to add the
correct type for the implicit object parameter
`Sema::getMoreSpecializedTemplate()` has new parameters for the object
type.

Fixes llvm#74494, fixes llvm#82509
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++20 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Clang][Sema][Concepts] Clang can't choose from a member and a non member operator incorrect overload resolution with deducing this
6 participants