[cfe-commits] r124591 - in /cfe/trunk: lib/Sema/SemaOverload.cpp test/SemaObjCXX/conversion-ranking.mm test/SemaObjCXX/overload.mm

Douglas Gregor dgregor at apple.com
Mon Jan 31 10:51:41 PST 2011


Author: dgregor
Date: Mon Jan 31 12:51:41 2011
New Revision: 124591

URL: http://llvm.org/viewvc/llvm-project?rev=124591&view=rev
Log:
Implement reasonable conversion ranking for Objective-C pointer
conversions (<rdar://problem/8592139>) 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).


Added:
    cfe/trunk/test/SemaObjCXX/conversion-ranking.mm
Modified:
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/test/SemaObjCXX/overload.mm

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=124591&r1=124590&r2=124591&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Mon Jan 31 12:51:41 2011
@@ -2651,9 +2651,6 @@
   //   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 @@
     QualType ToPointee2
       = ToType2->getAs<PointerType>()->getPointeeType().getUnqualifiedType();
 
-    const ObjCObjectType* FromIface1 = FromPointee1->getAs<ObjCObjectType>();
-    const ObjCObjectType* FromIface2 = FromPointee2->getAs<ObjCObjectType>();
-    const ObjCObjectType* ToIface1 = ToPointee1->getAs<ObjCObjectType>();
-    const ObjCObjectType* ToIface2 = ToPointee2->getAs<ObjCObjectType>();
-
     //   -- 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 @@
         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<ObjCObjectPointerType>();
+    const ObjCObjectPointerType *FromPtr2
+      = FromType2->getAs<ObjCObjectPointerType>();
+    const ObjCObjectPointerType *ToPtr1
+      = ToType1->getAs<ObjCObjectPointerType>();
+    const ObjCObjectPointerType *ToPtr2
+      = ToType2->getAs<ObjCObjectPointerType>();
+    
+    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() &&

Added: cfe/trunk/test/SemaObjCXX/conversion-ranking.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/conversion-ranking.mm?rev=124591&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjCXX/conversion-ranking.mm (added)
+++ cfe/trunk/test/SemaObjCXX/conversion-ranking.mm Mon Jan 31 12:51:41 2011
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+ at protocol P1
+ at end
+
+ at interface A <P1>
+ at end
+
+ at interface B : A
+ at end
+
+ at interface C : B
+ at end
+
+template<typename T>
+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<B*> toB, ConvertsTo<C*> 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<B*> toB, ConvertsTo<C*> toC, ConvertsToBoth toBoth) {
+  f4(toB);
+  f4(toC);
+  f4(toBoth);
+}
+
+void f5(id<P1>);
+
+void test_f5(ConvertsTo<B*> toB, ConvertsTo<C*> 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<P1>);
+
+void test_f6(B *b) {
+  int &ir = f6(b);
+}

Modified: cfe/trunk/test/SemaObjCXX/overload.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/overload.mm?rev=124591&r1=124590&r2=124591&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjCXX/overload.mm (original)
+++ cfe/trunk/test/SemaObjCXX/overload.mm Mon Jan 31 12:51:41 2011
@@ -31,8 +31,8 @@
 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 @@
   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 @@
 }
 
 // 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'}}
   }
 }
 





More information about the cfe-commits mailing list