]> granicus.if.org Git - clang/commitdiff
Implement implicit conversions for Objective-C specific types, e.g.,
authorDouglas Gregor <dgregor@apple.com>
Wed, 26 Nov 2008 23:31:11 +0000 (23:31 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 26 Nov 2008 23:31:11 +0000 (23:31 +0000)
converting a pointer to one Objective-C interface into a pointer to another
Objective-C interface, and conversions with 'id'. The semantics seems
to match GCC, although they seem somewhat ad hoc.

Fixed a few cases where we assumed the C++ definition of isObjectType,
but were getting the C definition, causing failures in trouble with
conversions to void pointers.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@60130 91177308-0d34-0410-b5e6-96231b3b80d8

Driver/clang.cpp
lib/Sema/SemaOverload.cpp
test/SemaObjCXX/overload.mm [new file with mode: 0644]

index dacdca75a9d2ad2cc5d4d77c5b057f25dff54ecd..0941ed4670b2976213cdf62b62aa2484b8df7316 100644 (file)
@@ -573,7 +573,7 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK,
   }
 
   if (Options.CPlusPlus)
-    Options.Blocks = 0;   
+    Options.Blocks = 0;
 }
 
 static llvm::cl::opt<bool>
index c6b028456fef23640bf0fd20bc152039ac6fb566..3d9ec8d6cb5141e3c875972bfcf2e7bd1b195e12 100644 (file)
@@ -709,6 +709,34 @@ bool Sema::IsFloatingPointPromotion(QualType FromType, QualType ToType)
   return false;
 }
 
+/// BuildSimilarlyQualifiedPointerType - In a pointer conversion from
+/// the pointer type FromPtr to a pointer to type ToPointee, with the
+/// same type qualifiers as FromPtr has on its pointee type. ToType,
+/// if non-empty, will be a pointer to ToType that may or may not have
+/// the right set of qualifiers on its pointee.
+static QualType 
+BuildSimilarlyQualifiedPointerType(const PointerType *FromPtr, 
+                                   QualType ToPointee, QualType ToType,
+                                   ASTContext &Context) {
+  QualType CanonFromPointee = Context.getCanonicalType(FromPtr->getPointeeType());
+  QualType CanonToPointee = Context.getCanonicalType(ToPointee);
+  unsigned Quals = CanonFromPointee.getCVRQualifiers();
+  
+  // Exact qualifier match -> return the pointer type we're converting to.  
+  if (CanonToPointee.getCVRQualifiers() == Quals) {
+    // ToType is exactly what we need. Return it.
+    if (ToType.getTypePtr())
+      return ToType;
+
+    // Build a pointer to ToPointee. It has the right qualifiers
+    // already.
+    return Context.getPointerType(ToPointee);
+  }
+
+  // Just build a canonical type that has the right qualifiers.
+  return Context.getPointerType(CanonToPointee.getQualifiedType(Quals));
+}
+
 /// IsPointerConversion - Determines whether the conversion of the
 /// expression From, which has the (possibly adjusted) type FromType,
 /// can be converted to the type ToType via a pointer conversion (C++
@@ -728,27 +756,20 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
     return true;
   }
 
+  // Beyond this point, both types need to be pointers.
+  const PointerType *FromTypePtr = FromType->getAsPointerType();
+  if (!FromTypePtr)
+    return false;
+
+  QualType FromPointeeType = FromTypePtr->getPointeeType();
+  QualType ToPointeeType = ToTypePtr->getPointeeType();
+
   // An rvalue of type "pointer to cv T," where T is an object type,
   // can be converted to an rvalue of type "pointer to cv void" (C++
   // 4.10p2).
-  if (FromType->isPointerType() &&
-      FromType->getAsPointerType()->getPointeeType()->isObjectType() &&
-      ToTypePtr->getPointeeType()->isVoidType()) {
-    // We need to produce a pointer to cv void, where cv is the same
-    // set of cv-qualifiers as we had on the incoming pointee type.
-    QualType toPointee = ToTypePtr->getPointeeType();
-    unsigned Quals = Context.getCanonicalType(FromType)->getAsPointerType()
-                   ->getPointeeType().getCVRQualifiers();
-
-    if (Context.getCanonicalType(ToTypePtr->getPointeeType()).getCVRQualifiers()
-         == Quals) {
-      // ToType is exactly the type we want. Use it.
-      ConvertedType = ToType;
-    } else {
-      // Build a new type with the right qualifiers.
-      ConvertedType 
-       = Context.getPointerType(Context.VoidTy.getQualifiedType(Quals));
-    }
+  if (FromPointeeType->isIncompleteOrObjectType() && ToPointeeType->isVoidType()) {
+    ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, ToPointeeType,
+                                                       ToType, Context);
     return true;
   }
 
@@ -765,32 +786,32 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
   //
   // Note that we do not check for ambiguity or inaccessibility
   // here. That is handled by CheckPointerConversion.
-  if (const PointerType *FromPtrType = FromType->getAsPointerType())
-    if (const PointerType *ToPtrType = ToType->getAsPointerType()) {
-      if (FromPtrType->getPointeeType()->isRecordType() &&
-          ToPtrType->getPointeeType()->isRecordType() &&
-          IsDerivedFrom(FromPtrType->getPointeeType(), 
-                        ToPtrType->getPointeeType())) {
-        // The conversion is okay. Now, we need to produce the type
-        // that results from this conversion, which will have the same
-        // qualifiers as the incoming type.
-        QualType CanonFromPointee 
-          = Context.getCanonicalType(FromPtrType->getPointeeType());
-        QualType ToPointee = ToPtrType->getPointeeType();
-        QualType CanonToPointee = Context.getCanonicalType(ToPointee);
-        unsigned Quals = CanonFromPointee.getCVRQualifiers();
-
-        if (CanonToPointee.getCVRQualifiers() == Quals) {
-          // ToType is exactly the type we want. Use it.
-          ConvertedType = ToType;
-        } else {
-          // Build a new type with the right qualifiers.
-          ConvertedType
-            = Context.getPointerType(CanonToPointee.getQualifiedType(Quals));
-        }
-        return true;
-      }
-    }
+  if (FromPointeeType->isRecordType() && ToPointeeType->isRecordType() &&
+      IsDerivedFrom(FromPointeeType, ToPointeeType)) {
+    ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, ToPointeeType,
+                                                       ToType, Context);
+    return true;
+  }
+
+  // Objective C++: We're able to convert from a pointer to an
+  // interface to a pointer to a different interface.
+  const ObjCInterfaceType* FromIface = FromPointeeType->getAsObjCInterfaceType();
+  const ObjCInterfaceType* ToIface = ToPointeeType->getAsObjCInterfaceType();
+  if (FromIface && ToIface && 
+      Context.canAssignObjCInterfaces(ToIface, FromIface)) {
+    ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, ToPointeeType,
+                                                       ToType, Context);
+    return true;
+  }
+
+  // Objective C++: We're able to convert between "id" and a pointer
+  // to any interface (in both directions).
+  if ((FromIface && Context.isObjCIdType(ToPointeeType))
+      || (ToIface && Context.isObjCIdType(FromPointeeType))) {
+    ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, ToPointeeType,
+                                                       ToType, Context);
+    return true;
+  } 
 
   return false;
 }
@@ -1125,6 +1146,17 @@ Sema::CompareStandardConversionSequences(const StandardConversionSequence& SCS1,
       return ImplicitConversionSequence::Better;
     else if (IsDerivedFrom(FromPointee1, FromPointee2))
       return ImplicitConversionSequence::Worse;
+
+    // Objective-C++: If one interface is more specific than the
+    // other, it is the better one.
+    const ObjCInterfaceType* FromIface1 = FromPointee1->getAsObjCInterfaceType();
+    const ObjCInterfaceType* FromIface2 = FromPointee2->getAsObjCInterfaceType();
+    if (FromIface1 && FromIface1) {
+      if (Context.canAssignObjCInterfaces(FromIface2, FromIface1))
+        return ImplicitConversionSequence::Better;
+      else if (Context.canAssignObjCInterfaces(FromIface1, FromIface2))
+        return ImplicitConversionSequence::Worse;
+    }
   }
 
   // Compare based on qualification conversions (C++ 13.3.3.2p3,
@@ -1247,7 +1279,9 @@ Sema::CompareQualificationConversions(const StandardConversionSequence& SCS1,
 
 /// CompareDerivedToBaseConversions - Compares two standard conversion
 /// sequences to determine whether they can be ranked based on their
-/// various kinds of derived-to-base conversions (C++ [over.ics.rank]p4b3).
+/// various kinds of derived-to-base conversions (C++
+/// [over.ics.rank]p4b3).  As part of these checks, we also look at
+/// conversions between Objective-C interface types.
 ImplicitConversionSequence::CompareKind
 Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1,
                                       const StandardConversionSequence& SCS2) {
@@ -1273,6 +1307,9 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1,
   //
   //   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 && 
@@ -1285,12 +1322,25 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1,
       = FromType2->getAsPointerType()->getPointeeType().getUnqualifiedType();
     QualType ToPointee2
       = ToType2->getAsPointerType()->getPointeeType().getUnqualifiedType();
+
+    const ObjCInterfaceType* FromIface1 = FromPointee1->getAsObjCInterfaceType();
+    const ObjCInterfaceType* FromIface2 = FromPointee2->getAsObjCInterfaceType();
+    const ObjCInterfaceType* ToIface1 = ToPointee1->getAsObjCInterfaceType();
+    const ObjCInterfaceType* ToIface2 = ToPointee2->getAsObjCInterfaceType();
+
     //   -- conversion of C* to B* is better than conversion of C* to A*,
     if (FromPointee1 == FromPointee2 && ToPointee1 != ToPointee2) {
       if (IsDerivedFrom(ToPointee1, ToPointee2))
         return ImplicitConversionSequence::Better;
       else if (IsDerivedFrom(ToPointee2, ToPointee1))
         return ImplicitConversionSequence::Worse;
+
+      if (ToIface1 && ToIface2) {
+        if (Context.canAssignObjCInterfaces(ToIface2, ToIface1))
+          return ImplicitConversionSequence::Better;
+        else if (Context.canAssignObjCInterfaces(ToIface1, ToIface2))
+          return ImplicitConversionSequence::Worse;
+      }
     }
 
     //   -- conversion of B* to A* is better than conversion of C* to A*,
@@ -1299,6 +1349,13 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1,
         return ImplicitConversionSequence::Better;
       else if (IsDerivedFrom(FromPointee1, FromPointee2))
         return ImplicitConversionSequence::Worse;
+      
+      if (FromIface1 && FromIface2) {
+        if (Context.canAssignObjCInterfaces(FromIface1, FromIface2))
+          return ImplicitConversionSequence::Better;
+        else if (Context.canAssignObjCInterfaces(FromIface2, FromIface1))
+          return ImplicitConversionSequence::Worse;
+      }
     }
   }
 
@@ -2268,7 +2325,7 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op,
     for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin();
          Ptr != CandidateTypes.pointer_end(); ++Ptr) {
       // Skip pointer types that aren't pointers to object types.
-      if (!(*Ptr)->getAsPointerType()->getPointeeType()->isObjectType())
+      if (!(*Ptr)->getAsPointerType()->getPointeeType()->isIncompleteOrObjectType())
         continue;
 
       QualType ParamTypes[2] = { 
diff --git a/test/SemaObjCXX/overload.mm b/test/SemaObjCXX/overload.mm
new file mode 100644 (file)
index 0000000..f4477d3
--- /dev/null
@@ -0,0 +1,39 @@
+// RUN clang -fsyntax-only -verify %s
+@interface A 
+@end
+
+@interface B : A
+@end
+
+int& f(A*);
+float& f(B*);
+void g(A*);
+
+int& h(A*);
+float& h(id);
+
+void test(A* a, B* b, id val) {
+  int& i1 = f(a);
+  float& f1 = f(b);
+  float& f2 = f(val);
+  g(a);
+  g(b);
+  g(val);
+  int& i2 = h(a);
+  float& f3 = h(val);
+  //  int& i3 = h(b); FIXME: we match GCC here, but shouldn't this work?
+}
+
+int& cv(A*);
+float& cv(const A*);
+int& cv2(void*);
+float& cv2(const void*);
+
+void cv_test(A* a, B* b, const A* ac, const B* bc) {
+  int &i1 = cv(a);
+  int &i2 = cv(b);
+  float &f1 = cv(ac);
+  float &f2 = cv(bc);
+  int& i3 = cv2(a);
+  float& f3 = cv2(ac);
+}