]> granicus.if.org Git - clang/commitdiff
Support more implicit conversions for Objective-C types. Addresses <rdar://problem...
authorDouglas Gregor <dgregor@apple.com>
Fri, 19 Dec 2008 19:13:09 +0000 (19:13 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 19 Dec 2008 19:13:09 +0000 (19:13 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61255 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/Sema.h
lib/Sema/SemaOverload.cpp
test/SemaObjCXX/overload.mm

index 1fac77549bf626f3387597fe854b5253c4e99f84..56b681c07055a8059ca670403496445737bf06cd 100644 (file)
@@ -383,6 +383,8 @@ public:
   bool IsFloatingPointPromotion(QualType FromType, QualType ToType);
   bool IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
                            QualType& ConvertedType, bool &IncompatibleObjC);
+  bool isObjCPointerConversion(QualType FromType, QualType ToType,
+                               QualType& ConvertedType, bool &IncompatibleObjC);
   bool CheckPointerConversion(Expr *From, QualType ToType);
   bool IsQualificationConversion(QualType FromType, QualType ToType);
   bool IsUserDefinedConversion(Expr *From, QualType ToType, 
index f03c7a4e901b30f64d54fcaedf2af9cc710af262..c719f56c4eef63886b78ebdfb7209e2551fc1d89 100644 (file)
@@ -763,6 +763,8 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
                                bool &IncompatibleObjC)
 {
   IncompatibleObjC = false;
+  if (isObjCPointerConversion(FromType, ToType, ConvertedType, IncompatibleObjC))
+    return true;
 
   // Blocks: Block pointers can be converted to void*.
   if (FromType->isBlockPointerType() && ToType->isPointerType() &&
@@ -777,13 +779,6 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
     return true;
   }
 
-  // Conversions with Objective-C's id<...>.
-  if ((FromType->isObjCQualifiedIdType() || ToType->isObjCQualifiedIdType()) &&
-      ObjCQualifiedIdTypesAreCompatible(ToType, FromType, /*compare=*/false)) {
-    ConvertedType = ToType;
-    return true;
-  }
-
   const PointerType* ToTypePtr = ToType->getAsPointerType();
   if (!ToTypePtr)
     return false;
@@ -805,7 +800,8 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
   // 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 (FromPointeeType->isIncompleteOrObjectType() && ToPointeeType->isVoidType()) {
+  if (FromPointeeType->isIncompleteOrObjectType() && 
+      ToPointeeType->isVoidType()) {
     ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, 
                                                        ToPointeeType,
                                                        ToType, Context);
@@ -833,6 +829,37 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
     return true;
   }
 
+  return false;
+}
+
+/// isObjCPointerConversion - Determines whether this is an
+/// Objective-C pointer conversion. Subroutine of IsPointerConversion,
+/// with the same arguments and return values.
+bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType, 
+                                   QualType& ConvertedType,
+                                   bool &IncompatibleObjC) {
+  if (!getLangOptions().ObjC1)
+    return false;
+
+  // Conversions with Objective-C's id<...>.
+  if ((FromType->isObjCQualifiedIdType() || ToType->isObjCQualifiedIdType()) &&
+      ObjCQualifiedIdTypesAreCompatible(ToType, FromType, /*compare=*/false)) {
+    ConvertedType = ToType;
+    return true;
+  }
+
+  const PointerType* ToTypePtr = ToType->getAsPointerType();
+  if (!ToTypePtr)
+    return false;
+
+  // 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();
+
   // 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();
@@ -877,7 +904,81 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
     return true;
   }
 
-  return false;
+  // If we have pointers to pointers, recursively check whether this
+  // is an Objective-C conversion.
+  if (FromPointeeType->isPointerType() && ToPointeeType->isPointerType() &&
+      isObjCPointerConversion(FromPointeeType, ToPointeeType, ConvertedType,
+                              IncompatibleObjC)) {
+    // We always complain about this conversion.
+    IncompatibleObjC = true;
+    ConvertedType = ToType;
+    return true;
+  }
+
+  // If we have pointers to functions, check whether the only
+  // differences in the argument and result types are in Objective-C
+  // pointer conversions. If so, we permit the conversion (but
+  // complain about it).
+  const FunctionTypeProto *FromFunctionType 
+    = FromPointeeType->getAsFunctionTypeProto();
+  const FunctionTypeProto *ToFunctionType
+    = ToPointeeType->getAsFunctionTypeProto();
+  if (FromFunctionType && ToFunctionType) {
+    // If the function types are exactly the same, this isn't an
+    // Objective-C pointer conversion.
+    if (Context.getCanonicalType(FromPointeeType)
+          == Context.getCanonicalType(ToPointeeType))
+      return false;
+
+    // Perform the quick checks that will tell us whether these
+    // function types are obviously different.
+    if (FromFunctionType->getNumArgs() != ToFunctionType->getNumArgs() ||
+        FromFunctionType->isVariadic() != ToFunctionType->isVariadic() ||
+        FromFunctionType->getTypeQuals() != ToFunctionType->getTypeQuals())
+      return false;
+
+    bool HasObjCConversion = false;
+    if (Context.getCanonicalType(FromFunctionType->getResultType())
+          == Context.getCanonicalType(ToFunctionType->getResultType())) {
+      // Okay, the types match exactly. Nothing to do.
+    } else if (isObjCPointerConversion(FromFunctionType->getResultType(),
+                                       ToFunctionType->getResultType(),
+                                       ConvertedType, IncompatibleObjC)) {
+      // Okay, we have an Objective-C pointer conversion.
+      HasObjCConversion = true;
+    } else {
+      // Function types are too different. Abort.
+      return false;
+    }
+     
+    // Check argument types.
+    for (unsigned ArgIdx = 0, NumArgs = FromFunctionType->getNumArgs();
+         ArgIdx != NumArgs; ++ArgIdx) {
+      QualType FromArgType = FromFunctionType->getArgType(ArgIdx);
+      QualType ToArgType = ToFunctionType->getArgType(ArgIdx);
+      if (Context.getCanonicalType(FromArgType)
+            == Context.getCanonicalType(ToArgType)) {
+        // Okay, the types match exactly. Nothing to do.
+      } else if (isObjCPointerConversion(FromArgType, ToArgType,
+                                         ConvertedType, IncompatibleObjC)) {
+        // Okay, we have an Objective-C pointer conversion.
+        HasObjCConversion = true;
+      } else {
+        // Argument types are too different. Abort.
+        return false;
+      }
+    }
+
+    if (HasObjCConversion) {
+      // We had an Objective-C conversion. Allow this pointer
+      // conversion, but complain about it.
+      ConvertedType = ToType;
+      IncompatibleObjC = true;
+      return true;
+    }
+  }
+
+  return false;  
 }
 
 /// CheckPointerConversion - Check the pointer conversion from the
index 0accb46ac0efe6372e088dcc6b23a3c1b243e4f3..41865ff3e826a2fc51a30556a78a13d49a58a033 100644 (file)
@@ -45,9 +45,12 @@ void test(A* a, B* b, id val) {
   //  int& i3 = h(b); FIXME: we match GCC here, but shouldn't this work?
 }
 
-void downcast_test(A* a) {
+void downcast_test(A* a, A** ap) {
   B* b = a; // expected-warning{{incompatible pointer types initializing 'B *', expected 'A *'}}
   b = a;  // expected-warning{{incompatible pointer types assigning 'B *', expected 'A *'}}
+
+  B** bp = ap; // expected-warning{{incompatible pointer types initializing 'B **', expected 'A **'}}
+  bp = ap; // expected-warning{{incompatible pointer types assigning 'B **', expected 'A **'}}
 }
 
 int& cv(A*);
@@ -73,3 +76,15 @@ void qualid_test(A *a, B *b, C *c) {
   int& i2 = qualid(b);
   float& f1 = qualid(c);
 }
+
+
+@class NSException;
+typedef struct {
+    void (*throw_exc)(id);
+}
+objc_exception_functions_t;
+
+void (*_NSExceptionRaiser(void))(NSException *) {
+    objc_exception_functions_t exc_funcs;
+    return exc_funcs.throw_exc; // expected-warning{{incompatible pointer types returning 'void (*)(NSException *)', expected 'void (*)(id)'}}
+}