From: Douglas Gregor Date: Mon, 31 Jan 2011 18:51:41 +0000 (+0000) Subject: Implement reasonable conversion ranking for Objective-C pointer X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=395cc3728047b999cafe2a640147a20f1a8d4696;p=clang Implement reasonable conversion ranking for Objective-C pointer conversions () for overload resolution. The conversion ranking mirrors C++'s conversion ranking fairly closely, except that we use a same pseudo-subtyping relationship employed by Objective-C pointer assignment rather than simple checking derived-to-base conversions. This change covers: - Conversions to pointers to a specific object type are better than conversions to 'id', 'Class', qualified 'id', or qualified 'Class' (note: GCC doesn't perform this ranking, but it matches C++'s rules for ranking conversions to void*). - Conversions to qualified 'id' or qualified 'Class' are better than conversions to 'id' or 'Class', respectively. - When two conversion sequences convert to the same type, rank the conversions based on the relationship between the types we're converting from. - When two conversion sequences convert from the same non-id, non-Class type, rank the conversions based on the relationship of the types we're converting to. (note: GCC allows this ranking even when converting from 'id', which is extremeley dangerous). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124591 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index e8a04bbd74..00cdca3bcf 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2651,9 +2651,6 @@ CompareDerivedToBaseConversions(Sema &S, // If class B is derived directly or indirectly from class A and // class C is derived directly or indirectly from B, // - // For Objective-C, we let A, B, and C also be Objective-C - // interfaces. - // Compare based on pointer conversions. if (SCS1.Second == ICK_Pointer_Conversion && SCS2.Second == ICK_Pointer_Conversion && @@ -2669,24 +2666,12 @@ CompareDerivedToBaseConversions(Sema &S, QualType ToPointee2 = ToType2->getAs()->getPointeeType().getUnqualifiedType(); - const ObjCObjectType* FromIface1 = FromPointee1->getAs(); - const ObjCObjectType* FromIface2 = FromPointee2->getAs(); - const ObjCObjectType* ToIface1 = ToPointee1->getAs(); - const ObjCObjectType* ToIface2 = ToPointee2->getAs(); - // -- conversion of C* to B* is better than conversion of C* to A*, if (FromPointee1 == FromPointee2 && ToPointee1 != ToPointee2) { if (S.IsDerivedFrom(ToPointee1, ToPointee2)) return ImplicitConversionSequence::Better; else if (S.IsDerivedFrom(ToPointee2, ToPointee1)) return ImplicitConversionSequence::Worse; - - if (ToIface1 && ToIface2) { - if (S.Context.canAssignObjCInterfaces(ToIface2, ToIface1)) - return ImplicitConversionSequence::Better; - else if (S.Context.canAssignObjCInterfaces(ToIface1, ToIface2)) - return ImplicitConversionSequence::Worse; - } } // -- conversion of B* to A* is better than conversion of C* to A*, @@ -2695,16 +2680,79 @@ CompareDerivedToBaseConversions(Sema &S, return ImplicitConversionSequence::Better; else if (S.IsDerivedFrom(FromPointee1, FromPointee2)) return ImplicitConversionSequence::Worse; + } + } else if (SCS1.Second == ICK_Pointer_Conversion && + SCS2.Second == ICK_Pointer_Conversion) { + const ObjCObjectPointerType *FromPtr1 + = FromType1->getAs(); + const ObjCObjectPointerType *FromPtr2 + = FromType2->getAs(); + const ObjCObjectPointerType *ToPtr1 + = ToType1->getAs(); + const ObjCObjectPointerType *ToPtr2 + = ToType2->getAs(); + + if (FromPtr1 && FromPtr2 && ToPtr1 && ToPtr2) { + // Apply the same conversion ranking rules for Objective-C pointer types + // that we do for C++ pointers to class types. However, we employ the + // Objective-C pseudo-subtyping relationship used for assignment of + // Objective-C pointer types. + bool FromAssignLeft + = S.Context.canAssignObjCInterfaces(FromPtr1, FromPtr2); + bool FromAssignRight + = S.Context.canAssignObjCInterfaces(FromPtr2, FromPtr1); + bool ToAssignLeft + = S.Context.canAssignObjCInterfaces(ToPtr1, ToPtr2); + bool ToAssignRight + = S.Context.canAssignObjCInterfaces(ToPtr2, ToPtr1); + + // A conversion to an a non-id object pointer type or qualified 'id' + // type is better than a conversion to 'id'. + if (ToPtr1->isObjCIdType() && + (ToPtr2->isObjCQualifiedIdType() || ToPtr2->getInterfaceDecl())) + return ImplicitConversionSequence::Worse; + if (ToPtr2->isObjCIdType() && + (ToPtr1->isObjCQualifiedIdType() || ToPtr1->getInterfaceDecl())) + return ImplicitConversionSequence::Better; + + // A conversion to a non-id object pointer type is better than a + // conversion to a qualified 'id' type + if (ToPtr1->isObjCQualifiedIdType() && ToPtr2->getInterfaceDecl()) + return ImplicitConversionSequence::Worse; + if (ToPtr2->isObjCQualifiedIdType() && ToPtr1->getInterfaceDecl()) + return ImplicitConversionSequence::Better; + + // A conversion to an a non-Class object pointer type or qualified 'Class' + // type is better than a conversion to 'Class'. + if (ToPtr1->isObjCClassType() && + (ToPtr2->isObjCQualifiedClassType() || ToPtr2->getInterfaceDecl())) + return ImplicitConversionSequence::Worse; + if (ToPtr2->isObjCClassType() && + (ToPtr1->isObjCQualifiedClassType() || ToPtr1->getInterfaceDecl())) + return ImplicitConversionSequence::Better; + + // A conversion to a non-Class object pointer type is better than a + // conversion to a qualified 'Class' type. + if (ToPtr1->isObjCQualifiedClassType() && ToPtr2->getInterfaceDecl()) + return ImplicitConversionSequence::Worse; + if (ToPtr2->isObjCQualifiedClassType() && ToPtr1->getInterfaceDecl()) + return ImplicitConversionSequence::Better; - if (FromIface1 && FromIface2) { - if (S.Context.canAssignObjCInterfaces(FromIface1, FromIface2)) - return ImplicitConversionSequence::Better; - else if (S.Context.canAssignObjCInterfaces(FromIface2, FromIface1)) - return ImplicitConversionSequence::Worse; - } + // -- "conversion of C* to B* is better than conversion of C* to A*," + if (S.Context.hasSameType(FromType1, FromType2) && + !FromPtr1->isObjCIdType() && !FromPtr1->isObjCClassType() && + (ToAssignLeft != ToAssignRight)) + return ToAssignLeft? ImplicitConversionSequence::Worse + : ImplicitConversionSequence::Better; + + // -- "conversion of B* to A* is better than conversion of C* to A*," + if (S.Context.hasSameUnqualifiedType(ToType1, ToType2) && + (FromAssignLeft != FromAssignRight)) + return FromAssignLeft? ImplicitConversionSequence::Better + : ImplicitConversionSequence::Worse; } } - + // Ranking of member-pointer types. if (SCS1.Second == ICK_Pointer_Member && SCS2.Second == ICK_Pointer_Member && FromType1->isMemberPointerType() && FromType2->isMemberPointerType() && diff --git a/test/SemaObjCXX/conversion-ranking.mm b/test/SemaObjCXX/conversion-ranking.mm new file mode 100644 index 0000000000..6c1408bf21 --- /dev/null +++ b/test/SemaObjCXX/conversion-ranking.mm @@ -0,0 +1,89 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +@protocol P1 +@end + +@interface A +@end + +@interface B : A +@end + +@interface C : B +@end + +template +struct ConvertsTo { + operator T() const; +}; + + +// conversion of C* to B* is better than conversion of C* to A*. +int &f0(A*); +float &f0(B*); + +void test_f0(C *c) { + float &fr1 = f0(c); +} + +// conversion of B* to A* is better than conversion of C* to A* +void f1(A*); + +struct ConvertsToBoth { +private: + operator C*() const; + +public: + operator B*() const; +}; + +void test_f1(ConvertsTo toB, ConvertsTo toC, ConvertsToBoth toBoth) { + f1(toB); + f1(toC); + f1(toBoth); +}; + +// A conversion to an a non-id object pointer type is better than a +// conversion to 'id'. +int &f2(A*); +float &f2(id); + +void test_f2(B *b) { + int &ir = f2(b); +} + +// A conversion to an a non-Class object pointer type is better than a +// conversion to 'Class'. +int &f3(A*); +float &f3(Class); + +void test_f3(B *b) { + int &ir = f3(b); +} + +// When both conversions convert to 'id' or 'Class', pick the most +// specific type to convert from. +void f4(id); + +void test_f4(ConvertsTo toB, ConvertsTo toC, ConvertsToBoth toBoth) { + f4(toB); + f4(toC); + f4(toBoth); +} + +void f5(id); + +void test_f5(ConvertsTo toB, ConvertsTo toC, ConvertsToBoth toBoth) { + f5(toB); + f5(toC); + f5(toBoth); +} + + +// A conversion to an a non-id object pointer type is better than a +// conversion to qualified 'id'. +int &f6(A*); +float &f6(id); + +void test_f6(B *b) { + int &ir = f6(b); +} diff --git a/test/SemaObjCXX/overload.mm b/test/SemaObjCXX/overload.mm index 750b6b183a..7e79a4249c 100644 --- a/test/SemaObjCXX/overload.mm +++ b/test/SemaObjCXX/overload.mm @@ -31,8 +31,8 @@ int& f(A*); // expected-note {{candidate}} float& f(B*); // expected-note {{candidate}} void g(A*); -int& h(A*); // expected-note{{candidate}} -float& h(id); // expected-note{{candidate}} +int& h(A*); +float& h(id); void test0(A* a, B* b, id val) { int& i1 = f(a); @@ -47,8 +47,7 @@ void test0(A* a, B* b, id val) { int& i2 = h(a); float& f3 = h(val); - // FIXME: we match GCC here, but shouldn't this work? - int& i3 = h(b); // expected-error{{call to 'h' is ambiguous}} + int& i3 = h(b); } void test1(A* a) { @@ -114,13 +113,12 @@ namespace test5 { } // rdar://problem/8592139 -// FIXME: this should resolve to the unavailable candidate namespace test6 { - void foo(id); // expected-note {{candidate}} + void foo(id); // expected-note{{candidate function}} void foo(A*) __attribute__((unavailable)); // expected-note {{explicitly made unavailable}} void test(B *b) { - foo(b); // expected-error {{call to 'foo' is ambiguous}} + foo(b); // expected-error {{call to unavailable function 'foo'}} } }