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