llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: None (offsetof)

<details>
<summary>Changes</summary>

CWG2803 "Overload resolution for reference binding of similar types"
CWG2958 "Overload resolution involving lvalue transformation and qualification 
conversion"

---

Patch is 24.00 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/132779.diff


6 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (+2) 
- (modified) clang/lib/Sema/SemaOverload.cpp (+139-90) 
- (modified) clang/test/CXX/drs/cwg28xx.cpp (+73) 
- (modified) clang/test/CXX/drs/cwg29xx.cpp (+22-2) 
- (modified) clang/test/SemaObjCXX/arc-overloading.mm (+11) 
- (modified) clang/www/cxx_dr_status.html (+45-5) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8182bccdd2da8..6cd99243b0f90 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -110,6 +110,8 @@ 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 `CWG2803 Overload resolution for reference binding of similar 
types <https://cplusplus.github.io/CWG/issues/2803>`_,
+  as amended by the proposed resolution of `CWG2958 Overload resolution 
involving lvalue transformation and qualification conversion 
<https://cplusplus.github.io/CWG/issues/2958>`_
 - Implemented `CWG2918 Consideration of constraints for address of overloaded `
   `function <https://cplusplus.github.io/CWG/issues/2918.html>`_
 
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 6d8006b35dcf4..3bf9b800df1d2 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -116,6 +116,11 @@ CompareQualificationConversions(Sema &S,
                                 const StandardConversionSequence& SCS1,
                                 const StandardConversionSequence& SCS2);
 
+static ImplicitConversionSequence::CompareKind
+CompareReferenceBindingConversions(Sema &S,
+                                   const StandardConversionSequence &SCS1,
+                                   const StandardConversionSequence &SCS2);
+
 static ImplicitConversionSequence::CompareKind
 CompareDerivedToBaseConversions(Sema &S, SourceLocation Loc,
                                 const StandardConversionSequence& SCS1,
@@ -4408,19 +4413,15 @@ compareStandardConversionSubsets(ASTContext &Context,
 static bool
 isBetterReferenceBindingKind(const StandardConversionSequence &SCS1,
                              const StandardConversionSequence &SCS2) {
-  // C++0x [over.ics.rank]p3b4:
-  //   -- S1 and S2 are reference bindings (8.5.3) and neither refers to an
-  //      implicit object parameter of a non-static member function declared
-  //      without a ref-qualifier, and *either* S1 binds an rvalue reference
-  //      to an rvalue and S2 binds an lvalue reference *or S1 binds an
-  //      lvalue reference to a function lvalue and S2 binds an rvalue
-  //      reference*.
-  //
-  // FIXME: Rvalue references. We're going rogue with the above edits,
-  // because the semantics in the current C++0x working paper (N3225 at the
-  // time of this writing) break the standard definition of std::forward
-  // and std::reference_wrapper when dealing with references to functions.
-  // Proposed wording changes submitted to CWG for consideration.
+  // C++2c [over.ics.rank] p3.2.3 - 3.2.4:
+  //   * S1 and S2 include reference bindings and neither refers to an
+  //     implicit object parameter of a non-static member function
+  //     declared without a ref-qualifier, and S1 binds an rvalue
+  //     reference to an rvalue and S2 binds an lvalue reference
+  //     or, if not that,
+  //   * S1 and S2 include reference bindings and S1 binds an lvalue
+  //     reference to an lvalue of function type and S2 binds an rvalue
+  //     reference to an lvalue of function type
   if (SCS1.BindsImplicitObjectArgumentWithoutRefQualifier ||
       SCS2.BindsImplicitObjectArgumentWithoutRefQualifier)
     return false;
@@ -4580,52 +4581,19 @@ CompareStandardConversionSequences(Sema &S, 
SourceLocation Loc,
     // Check for a better reference binding based on the kind of bindings.
     if (isBetterReferenceBindingKind(SCS1, SCS2))
       return ImplicitConversionSequence::Better;
-    else if (isBetterReferenceBindingKind(SCS2, SCS1))
+    if (isBetterReferenceBindingKind(SCS2, SCS1))
       return ImplicitConversionSequence::Worse;
   }
 
-  // Compare based on qualification conversions (C++ 13.3.3.2p3,
-  // bullet 3).
-  if (ImplicitConversionSequence::CompareKind QualCK
-        = CompareQualificationConversions(S, SCS1, SCS2))
+  // Compare based on qualification conversions
+  // (C++2c [over.ics.rank] p3.2.5).
+  if (auto QualCK = CompareQualificationConversions(S, SCS1, SCS2))
     return QualCK;
 
-  if (SCS1.ReferenceBinding && SCS2.ReferenceBinding) {
-    // C++ [over.ics.rank]p3b4:
-    //   -- S1 and S2 are reference bindings (8.5.3), and the types to
-    //      which the references refer are the same type except for
-    //      top-level cv-qualifiers, and the type to which the reference
-    //      initialized by S2 refers is more cv-qualified than the type
-    //      to which the reference initialized by S1 refers.
-    QualType T1 = SCS1.getToType(2);
-    QualType T2 = SCS2.getToType(2);
-    T1 = S.Context.getCanonicalType(T1);
-    T2 = S.Context.getCanonicalType(T2);
-    Qualifiers T1Quals, T2Quals;
-    QualType UnqualT1 = S.Context.getUnqualifiedArrayType(T1, T1Quals);
-    QualType UnqualT2 = S.Context.getUnqualifiedArrayType(T2, T2Quals);
-    if (UnqualT1 == UnqualT2) {
-      // Objective-C++ ARC: If the references refer to objects with different
-      // lifetimes, prefer bindings that don't change lifetime.
-      if (SCS1.ObjCLifetimeConversionBinding !=
-                                          SCS2.ObjCLifetimeConversionBinding) {
-        return SCS1.ObjCLifetimeConversionBinding
-                                           ? ImplicitConversionSequence::Worse
-                                           : 
ImplicitConversionSequence::Better;
-      }
-
-      // If the type is an array type, promote the element qualifiers to the
-      // type for comparison.
-      if (isa<ArrayType>(T1) && T1Quals)
-        T1 = S.Context.getQualifiedType(UnqualT1, T1Quals);
-      if (isa<ArrayType>(T2) && T2Quals)
-        T2 = S.Context.getQualifiedType(UnqualT2, T2Quals);
-      if (T2.isMoreQualifiedThan(T1, S.getASTContext()))
-        return ImplicitConversionSequence::Better;
-      if (T1.isMoreQualifiedThan(T2, S.getASTContext()))
-        return ImplicitConversionSequence::Worse;
-    }
-  }
+  // Compare based on target types of reference bindings
+  // (C++2c [over.ics.rank] p3.2.6).
+  if (auto RefCK = CompareReferenceBindingConversions(S, SCS1, SCS2))
+    return RefCK;
 
   // In Microsoft mode (below 19.28), prefer an integral conversion to a
   // floating-to-integral conversion if the integral conversion
@@ -4704,64 +4672,78 @@ CompareStandardConversionSequences(Sema &S, 
SourceLocation Loc,
 
 /// CompareQualificationConversions - Compares two standard conversion
 /// sequences to determine whether they can be ranked based on their
-/// qualification conversions (C++ 13.3.3.2p3 bullet 3).
+/// qualification conversions (C++2c [over.ics.rank] p3.2.5).
 static ImplicitConversionSequence::CompareKind
 CompareQualificationConversions(Sema &S,
                                 const StandardConversionSequence& SCS1,
                                 const StandardConversionSequence& SCS2) {
-  // C++ [over.ics.rank]p3:
-  //  -- S1 and S2 differ only in their qualification conversion and
-  //     yield similar types T1 and T2 (C++ 4.4), respectively, [...]
-  // [C++98]
-  //     [...] and the cv-qualification signature of type T1 is a proper subset
-  //     of the cv-qualification signature of type T2, and S1 is not the
-  //     deprecated string literal array-to-pointer conversion (4.2).
-  // [C++2a]
-  //     [...] where T1 can be converted to T2 by a qualification conversion.
-  if (SCS1.First != SCS2.First || SCS1.Second != SCS2.Second ||
-      SCS1.Third != SCS2.Third || SCS1.Third != ICK_Qualification)
+  // C++2c [over.ics.rank] p3.2.5:
+  //   * S1 and S2 differ only in their qualification conversion
+  //     [CWG2958: ignoring any Lvalue Transformation,] and yield
+  //     similar types T1 and T2, respectively (where a standard
+  //     conversion sequence that is a reference binding is considered to
+  //     yield the cv-unqualified referenced type), where T1 and T2 are
+  //     not the same type, and const T2 is reference-compatible with T1
+  if (SCS1.Second != SCS2.Second || SCS1.Third != SCS2.Third ||
+      SCS1.Third != ICK_Qualification)
     return ImplicitConversionSequence::Indistinguishable;
 
-  // FIXME: the example in the standard doesn't use a qualification
-  // conversion (!)
   QualType T1 = SCS1.getToType(2);
   QualType T2 = SCS2.getToType(2);
   T1 = S.Context.getCanonicalType(T1);
   T2 = S.Context.getCanonicalType(T2);
   assert(!T1->isReferenceType() && !T2->isReferenceType());
-  Qualifiers T1Quals, T2Quals;
-  QualType UnqualT1 = S.Context.getUnqualifiedArrayType(T1, T1Quals);
-  QualType UnqualT2 = S.Context.getUnqualifiedArrayType(T2, T2Quals);
+  T1 = S.Context.getUnqualifiedArrayType(T1);
+  T2 = S.Context.getUnqualifiedArrayType(T2);
 
   // If the types are the same, we won't learn anything by unwrapping
   // them.
-  if (UnqualT1 == UnqualT2)
+  if (T1 == T2)
     return ImplicitConversionSequence::Indistinguishable;
 
   // Don't ever prefer a standard conversion sequence that uses the deprecated
-  // string literal array to pointer conversion.
+  // string literal array to pointer conversion (C++03 [over.ics.rank] p3.1.3).
   bool CanPick1 = !SCS1.DeprecatedStringLiteralToCharPtr;
   bool CanPick2 = !SCS2.DeprecatedStringLiteralToCharPtr;
 
   // Objective-C++ ARC:
   //   Prefer qualification conversions not involving a change in lifetime
   //   to qualification conversions that do change lifetime.
-  if (SCS1.QualificationIncludesObjCLifetime &&
-      !SCS2.QualificationIncludesObjCLifetime)
-    CanPick1 = false;
-  if (SCS2.QualificationIncludesObjCLifetime &&
-      !SCS1.QualificationIncludesObjCLifetime)
-    CanPick2 = false;
+  if (SCS1.QualificationIncludesObjCLifetime !=
+      SCS2.QualificationIncludesObjCLifetime) {
+    CanPick1 &= SCS2.QualificationIncludesObjCLifetime;
+    CanPick2 &= SCS1.QualificationIncludesObjCLifetime;
+  }
+
+  // If exactly one of T1 and T2 is an array of unknown bound,
+  // the other type is not reference-compatible with it.
+  bool T1IsIncompleteArray = T1->isIncompleteArrayType();
+  bool T2IsIncompleteArray = T2->isIncompleteArrayType();
+  if (T1IsIncompleteArray != T2IsIncompleteArray) {
+    CanPick1 &= T2IsIncompleteArray;
+    CanPick2 &= T1IsIncompleteArray;
+  }
 
+  bool PrevT1QualsIncludeConst = true;
+  bool PrevT2QualsIncludeConst = true;
+  bool IsTopLevel = true;
   bool ObjCLifetimeConversion;
-  if (CanPick1 &&
-      !S.IsQualificationConversion(T1, T2, false, ObjCLifetimeConversion))
-    CanPick1 = false;
-  // FIXME: In Objective-C ARC, we can have qualification conversions in both
-  // directions, so we can't short-cut this second check in general.
-  if (CanPick2 &&
-      !S.IsQualificationConversion(T2, T1, false, ObjCLifetimeConversion))
-    CanPick2 = false;
+  while ((CanPick1 || CanPick2) && S.Context.UnwrapSimilarTypes(T1, T2) &&
+         (T1 != T2)) {
+    CanPick1 = CanPick1 &&
+               isQualificationConversionStep(
+                   T1, T2, /*CStyle=*/false, IsTopLevel,
+                   PrevT2QualsIncludeConst, ObjCLifetimeConversion, S.Context);
+    CanPick2 = CanPick2 &&
+               isQualificationConversionStep(
+                   T2, T1, /*CStyle=*/false, IsTopLevel,
+                   PrevT1QualsIncludeConst, ObjCLifetimeConversion, S.Context);
+    IsTopLevel = false;
+  }
+
+  if (!S.Context.hasSameUnqualifiedType(T1, T2))
+    // T1 and T2 are not similar.
+    return ImplicitConversionSequence::Indistinguishable;
 
   if (CanPick1 != CanPick2)
     return CanPick1 ? ImplicitConversionSequence::Better
@@ -4769,6 +4751,73 @@ CompareQualificationConversions(Sema &S,
   return ImplicitConversionSequence::Indistinguishable;
 }
 
+/// CompareReferenceBindingConversions - Compare two standard conversion
+/// sequences involving reference bindings to determine whether they can
+/// be ranked based on the referenced types (C++2c [over.ics.rank] p3.2.6).
+static ImplicitConversionSequence::CompareKind
+CompareReferenceBindingConversions(Sema &S,
+                                   const StandardConversionSequence &SCS1,
+                                   const StandardConversionSequence &SCS2) {
+  // C++2c [over.ics.rank] p3.2.6:
+  //   * S1 and S2 bind "reference to T1" and "reference to T2",
+  //     respectively, where T1 and T2 are not the same type,
+  //     and T2 is reference-compatible with T1
+  // Derived-to-base and qualification conversions are handled by previous
+  // bullets; this bullet only applies when T1 and T2 differ solely in their
+  // top-level cv-qualifiers and/or the presence of a major array bound.
+  if (!SCS1.ReferenceBinding || !SCS2.ReferenceBinding)
+    return ImplicitConversionSequence::Indistinguishable;
+
+  QualType T1 = SCS1.getToType(2);
+  QualType T2 = SCS2.getToType(2);
+
+  Qualifiers T1Quals, T2Quals;
+  T1 = S.Context.getUnqualifiedArrayType(T1, T1Quals);
+  T2 = S.Context.getUnqualifiedArrayType(T2, T2Quals);
+  int Result = 0;
+
+  // Compare array bounds.
+  if (auto *T1Arr = S.Context.getAsArrayType(T1)) {
+    auto *T2Arr = S.Context.getAsArrayType(T2);
+    if (!T2Arr)
+      return ImplicitConversionSequence::Indistinguishable;
+
+    bool T1IsIncomplete = isa<IncompleteArrayType>(T1Arr);
+    bool T2IsIncomplete = isa<IncompleteArrayType>(T2Arr);
+    if (T1IsIncomplete || T2IsIncomplete) {
+      T1 = T1Arr->getElementType();
+      T2 = T2Arr->getElementType();
+      Result = T2IsIncomplete - T1IsIncomplete;
+    }
+  }
+
+  if (!S.Context.hasSameType(T1, T2))
+    return ImplicitConversionSequence::Indistinguishable;
+
+  // Objective-C++ ARC: If the references refer to objects with different
+  // lifetimes, prefer bindings that don't change lifetime.
+  if (SCS1.ObjCLifetimeConversionBinding !=
+      SCS2.ObjCLifetimeConversionBinding) {
+    return SCS1.ObjCLifetimeConversionBinding
+               ? ImplicitConversionSequence::Worse
+               : ImplicitConversionSequence::Better;
+  }
+
+  // Compare cv-qualifiers.
+  bool T1Compatible = T1Quals.compatiblyIncludes(T2Quals, S.Context);
+  bool T2Compatible = T2Quals.compatiblyIncludes(T1Quals, S.Context);
+  if (!T1Compatible && !T2Compatible)
+    return ImplicitConversionSequence::Indistinguishable;
+
+  Result += T2Compatible - T1Compatible;
+
+  if (Result > 0)
+    return ImplicitConversionSequence::Better;
+  if (Result < 0)
+    return ImplicitConversionSequence::Worse;
+  return ImplicitConversionSequence::Indistinguishable;
+}
+
 /// CompareDerivedToBaseConversions - Compares two standard conversion
 /// sequences to determine whether they can be ranked based on their
 /// various kinds of derived-to-base conversions (C++
@@ -5239,9 +5288,6 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
                                     ? ICK_Compatible_Conversion
                                     : ICK_Identity;
     ICS.Standard.Dimension = ICK_Identity;
-    // FIXME: As a speculative fix to a defect introduced by CWG2352, we rank
-    // a reference binding that performs a non-top-level qualification
-    // conversion as a qualification conversion, not as an identity conversion.
     ICS.Standard.Third = (RefConv &
                               Sema::ReferenceConversions::NestedQualification)
                              ? ICK_Qualification
@@ -5250,6 +5296,9 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
     ICS.Standard.setToType(0, T2);
     ICS.Standard.setToType(1, T1);
     ICS.Standard.setToType(2, T1);
+    ICS.Standard.QualificationIncludesObjCLifetime =
+        RefConv & Sema::ReferenceConversions::NestedQualification &&
+        RefConv & Sema::ReferenceConversions::ObjCLifetime;
     ICS.Standard.ReferenceBinding = true;
     ICS.Standard.DirectBinding = BindsDirectly;
     ICS.Standard.IsLvalueReference = !isRValRef;
diff --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index b32e649374893..ed07081765053 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -6,6 +6,79 @@
 // RUN: %clang_cc1 -std=c++23 -pedantic-errors 
-verify=expected,since-cxx11,cxx11-23,since-cxx20,since-cxx23 %s
 // RUN: %clang_cc1 -std=c++2c -pedantic-errors 
-verify=expected,since-cxx11,since-cxx20,since-cxx23,since-cxx26 %s
 
+namespace cwg2803 { // cwg2803: 21
+
+int a[1], *p, *ap[1];
+
+void f1(void*);
+int f1(const volatile int* const&);
+int i1 = f1((int*)0);
+
+void f2(const volatile void* const&);
+int f2(void*);
+int i2 = f2((int*)0);
+
+void f3(const volatile int*);
+int f3(const int*);
+int i3 = f3((int*)0);
+
+void f4(const volatile int* const&);
+int f4(const int* const volatile&);
+int i4 = f4(p);
+
+void f5(const int* const volatile&);
+int f5(const int* const&);
+int i5 = f5(p);
+
+void f6(const volatile int* const (&)[1]);
+int f6(const int* const volatile (&)[1]);
+int i6 = f6(ap);
+
+void f7(const int* const volatile (&)[1]);
+int f7(const int* const (&)[1]);
+int i7 = f7(ap);
+
+int f8(const int* const (&)[]);           // since-cxx20-note {{candidate 
function}}
+int f8(const volatile int* const (&)[1]); // since-cxx20-note {{candidate 
function}}
+int i8 = f8(ap); // since-cxx20-error {{ambiguous}}
+
+void f9(const volatile int* const (&)[]);
+int f9(const int* const volatile (&)[1]);
+int i9 = f9(ap);
+
+void f10(int (&)[]);
+int f10(int (&)[1]);
+int i10 = f10(a);
+
+int f11(int (&)[]);        // since-cxx20-note {{candidate function}}
+int f11(const int (&)[1]); // since-cxx20-note {{candidate function}}
+int i11 = f11(a); // since-cxx20-error {{ambiguous}}
+
+int f12(const int (&)[]);     // since-cxx20-note {{candidate function}}
+int f12(volatile int (&)[1]); // since-cxx20-note {{candidate function}}
+int i12 = f12(a); // since-cxx20-error {{ambiguous}}
+
+#if __cpp_rvalue_references >= 200610
+void f13(const int* const&&);
+int f13(int* const&);
+int i13 = f13((int*)0);
+
+void f14(const int* const&);
+int f14(const volatile int* const volatile&&);
+int i14 = f14((int*)0);
+
+constexpr int f15(const volatile int (&&)[]) {
+       return 1;
+}
+constexpr int f15(const int (&)[1]) {
+       return 2;
+}
+constexpr int i15 = f15(static_cast<int (&&)[1]>(a));
+static_assert(i15 == 2, ""); // since-cxx20-error {{static assertion failed}}
+// since-cxx20-note@-1 {{expression evaluates to '1 == 2'}}
+#endif
+
+} // namespace cwg2803
 
 int main() {} // required for cwg2811
 
diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp
index f9c2e9ecf4618..2ccded1e45524 100644
--- a/clang/test/CXX/drs/cwg29xx.cpp
+++ b/clang/test/CXX/drs/cwg29xx.cpp
@@ -6,8 +6,6 @@
 // RUN: %clang_cc1 -std=c++23 -pedantic-errors 
-verify=expected,since-cxx11,since-cxx20,since-cxx23 %s
 // RUN: %clang_cc1 -std=c++2c -pedantic-errors 
-verify=expected,since-cxx11,since-cxx20,since-cxx23,since-cxx26 %s
 
-// cxx98-no-diagnostics
-
 namespace cwg2913 { // cwg2913: 20
 
 #if __cplusplus >= 202002L
@@ -171,3 +169,25 @@ constexpr U _ = nondeterministic(true);
 //   since-cxx26-note@-3 {{in call to 'nondeterministic(true)'}}
 #endif
 } // namespace cwg2922
+
+namespace cwg2958 { // cwg2958: 21 open 2024-11-10
+
+int *ap[1];
+
+void f1(const volatile int*);
+int f1(const int* const&);
+int i1 = f1((int*)0);
+
+void f2(const volatile int* const&);
+int f2(const int*);
+int i2 = f2((int*)0);
+
+int f3(const int* const*);                // expected-note {{candidate 
function}}
+int f3(const volatile int* const (&)[1]); // expected-note {{candidate 
function}}
+int i3 = f3(ap); // expected-error {{ambiguous}}
+
+int f4(const volatile int* const*); // expected-note {{candidate function}}
+int f4(const int* const (&)[1]);    // expected-note {{candidate function}}
+int i4 = f4(ap); // expected-error {{ambiguous}}
+
+} // namespace cwg2958
diff --git a/clang/test/SemaObjCXX/arc-overloading.mm 
b/clang/test/SemaObjCXX/arc-overloading.mm
index 8ee01ad46c675..7fe1454de6c8f 100644
--- a/clang/test/SemaObjCXX/arc-overloading.mm
+++ b/clang/test/SemaObjCXX/arc-overloading.mm
@@ -204,6 +204,17 @@ void test_f11() {
   float &fr2a = f11(weak_id); // expected-error {{no match}}
 }
 
+int &f12(const __strong id *);
+float &f12(const __autoreleasing id *const &);
+
+void test_f12() {
+  __strong id *strong_id;
+  __autoreleasing id *autoreleasing_id;
+
+  int &ir = f12(strong_id);
+  float &fr = f12(autoreleasing_id);
+}
+
 void f9790531(void *inClientData); // expected-note {{can...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/132779
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to