]> granicus.if.org Git - clang/commitdiff
Implement objective-c++'s block pointer type matching involving
authorFariborz Jahanian <fjahanian@apple.com>
Sat, 12 Feb 2011 19:07:46 +0000 (19:07 +0000)
committerFariborz Jahanian <fjahanian@apple.com>
Sat, 12 Feb 2011 19:07:46 +0000 (19:07 +0000)
types which are contravariance in argument types and covariance
in return types. // rdar://8979379.

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

include/clang/Sema/Overload.h
include/clang/Sema/Sema.h
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaOverload.cpp
test/CodeGenObjCXX/blocks.mm [new file with mode: 0644]
test/SemaCXX/block-call.cpp [new file with mode: 0644]
test/SemaObjCXX/blocks.mm

index fb7f5f129d4db6407a90496bd686e4716f9b985a..3ce3513c21ae87f2426c133908b31a0c8eff3e70 100644 (file)
@@ -75,6 +75,7 @@ namespace clang {
     ICK_Vector_Conversion,     ///< Vector conversions
     ICK_Vector_Splat,          ///< A vector splat from an arithmetic type
     ICK_Complex_Real,          ///< Complex-real conversions (C99 6.3.1.7)
+    ICK_Block_Pointer_Conversion,    ///< Block Pointer conversions 
     ICK_Num_Conversion_Kinds   ///< The number of conversion kinds
   };
 
index 4731ad8d27b0b7e0bfcdaf5b71e858856907b92a..cfd21df753b81d11a9af8813e8e14fdec21ca427 100644 (file)
@@ -1019,6 +1019,8 @@ public:
                            QualType& ConvertedType, bool &IncompatibleObjC);
   bool isObjCPointerConversion(QualType FromType, QualType ToType,
                                QualType& ConvertedType, bool &IncompatibleObjC);
+  bool IsBlockPointerConversion(QualType FromType, QualType ToType,
+                                QualType& ConvertedType);
   bool FunctionArgTypesAreEqual(const FunctionProtoType *OldType, 
                                 const FunctionProtoType *NewType);
   
index f886e17d5469fadc86e65100cb1a40b1defe4124..e9f595549f2f263aedaccde191441e55de52f20a 100644 (file)
@@ -2199,7 +2199,12 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
       }
     }
     break;
-
+  
+  case ICK_Block_Pointer_Conversion: {
+      ImpCastExprToType(From, ToType.getUnqualifiedType(), CK_BitCast, VK_RValue);
+      break;
+    }
+      
   case ICK_Lvalue_To_Rvalue:
   case ICK_Array_To_Pointer:
   case ICK_Function_To_Pointer:
index 42e24116bfe28d62a0d436ffb34de22b4fb6c649..70cc56605925d3dfcb842de75aa1df252ce7bda0 100644 (file)
@@ -1166,6 +1166,8 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
     // Floating-integral conversions (C++ 4.9).
     SCS.Second = ICK_Floating_Integral;
     FromType = ToType.getUnqualifiedType();
+  } else if (S.IsBlockPointerConversion(FromType, ToType, FromType)) {
+               SCS.Second = ICK_Block_Pointer_Conversion;
   } else if (S.IsPointerConversion(From, FromType, ToType, InOverloadResolution,
                                    FromType, IncompatibleObjC)) {
     // Pointer conversions (C++ 4.10).
@@ -1782,6 +1784,92 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType,
   return false;
 }
 
+bool Sema::IsBlockPointerConversion(QualType FromType, QualType ToType,
+                                    QualType& ConvertedType) {
+  QualType ToPointeeType;
+  if (const BlockPointerType *ToBlockPtr =
+        ToType->getAs<BlockPointerType>())
+    ToPointeeType = ToBlockPtr->getPointeeType();
+  else
+    return false;
+  
+  QualType FromPointeeType;
+  if (const BlockPointerType *FromBlockPtr =
+      FromType->getAs<BlockPointerType>())
+    FromPointeeType = FromBlockPtr->getPointeeType();
+  else
+    return false;
+  // We have pointer to blocks, check whether the only
+  // differences in the argument and result types are in Objective-C
+  // pointer conversions. If so, we permit the conversion.
+  
+  const FunctionProtoType *FromFunctionType
+    = FromPointeeType->getAs<FunctionProtoType>();
+  const FunctionProtoType *ToFunctionType
+    = ToPointeeType->getAs<FunctionProtoType>();
+  
+  if (FromFunctionType && ToFunctionType) {
+    if (Context.getCanonicalType(FromPointeeType)
+          == Context.getCanonicalType(ToPointeeType))
+      return true;
+    
+    // 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 IncompatibleObjC = false;
+    if (Context.getCanonicalType(FromFunctionType->getResultType())
+          == Context.getCanonicalType(ToFunctionType->getResultType())) {
+      // Okay, the types match exactly. Nothing to do.
+    } else {
+      QualType RHS = FromFunctionType->getResultType();
+      QualType CanRHS = Context.getCanonicalType(RHS);
+      QualType LHS = ToFunctionType->getResultType();
+      QualType CanLHS = Context.getCanonicalType(LHS);
+      if (!CanRHS->isRecordType() &&
+          !CanRHS.hasQualifiers() && CanLHS.hasQualifiers())
+        CanLHS = CanLHS.getUnqualifiedType();
+
+      if (Context.getCanonicalType(CanRHS)
+          == Context.getCanonicalType(CanLHS)) {
+        // OK exact match.
+      } else if (isObjCPointerConversion(CanRHS, CanLHS,
+                                  ConvertedType, IncompatibleObjC)) {
+      if (IncompatibleObjC)
+        return false;
+      // Okay, we have an Objective-C pointer conversion.
+      }
+      else
+        return false;
+    }
+    
+    // Check argument types.
+    for (unsigned ArgIdx = 0, NumArgs = FromFunctionType->getNumArgs();
+         ArgIdx != NumArgs; ++ArgIdx) {
+      IncompatibleObjC = false;
+      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(ToArgType, FromArgType,
+                                         ConvertedType, IncompatibleObjC)) {
+        if (IncompatibleObjC)
+          return false;
+        // Okay, we have an Objective-C pointer conversion.
+      } else
+        // Argument types are too different. Abort.
+        return false;
+    }
+    ConvertedType = ToType;
+    return true;
+  }
+  return false;
+}
+
 /// FunctionArgTypesAreEqual - This routine checks two function proto types
 /// for equlity of their argument types. Caller has already checked that
 /// they have same number of arguments. This routine assumes that Objective-C
diff --git a/test/CodeGenObjCXX/blocks.mm b/test/CodeGenObjCXX/blocks.mm
new file mode 100644 (file)
index 0000000..ffb916b
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -x objective-c++ -fblocks -triple x86_64-apple-darwin %s
+// rdar://8979379
+
+@interface A
+@end
+
+@interface B : A
+@end
+
+void f(int (^bl)(B* b));
+
+// Test1
+void g() {
+  f(^(A* a) { return 0; });
+}
+
+// Test2
+void g1() {
+  int (^bl)(B* b) = ^(A* a) { return 0; };
+}
+
+// Test3
+@protocol NSObject;
+
+void bar(id(^)(void));
+
+void foo(id <NSObject>(^objectCreationBlock)(void)) {
+    return bar(objectCreationBlock);
+}
+
diff --git a/test/SemaCXX/block-call.cpp b/test/SemaCXX/block-call.cpp
new file mode 100644 (file)
index 0000000..d519911
--- /dev/null
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s -fblocks
+
+int (*FP)();
+int (^IFP) ();
+int (^II) (int);
+int main() {
+  int (*FPL) (int) = FP; // expected-error {{cannot initialize a variable of type 'int (*)(int)' with an lvalue of type 'int (*)()'}} 
+
+  // For Blocks, the ASTContext::typesAreBlockCompatible() makes sure this is an error.
+  int (^PFR) (int) = IFP; // expected-error {{cannot initialize a variable of type 'int (^)(int)' with an lvalue of type 'int (^)()'}}
+  PFR = II;       // OK
+
+  int (^IFP) () = PFR; // OK
+
+
+  const int (^CIC) () = IFP; // OK -  initializing 'const int (^)()' with an expression of type 'int (^)()'}}
+
+  const int (^CICC) () = CIC;
+
+
+  int * const (^IPCC) () = 0;
+
+  int * const (^IPCC1) () = IPCC;
+
+  int * (^IPCC2) () = IPCC;       // expected-error  {{cannot initialize a variable of type 'int *(^)()' with an lvalue of type 'int *const (^)()'}}
+
+  int (^IPCC3) (const int) = PFR;
+
+  int (^IPCC4) (int, char (^CArg) (double));
+
+  int (^IPCC5) (int, char (^CArg) (double)) = IPCC4;
+
+  int (^IPCC6) (int, char (^CArg) (float))  = IPCC4; // expected-error {{cannot initialize a variable of type 'int (^)(int, char (^)(float))' with an lvalue of type}}
+
+  IPCC2 = 0;
+  IPCC2 = 1; 
+  int (^x)() = 0;
+  int (^y)() = 3;   // expected-error {{cannot initialize a variable of type 'int (^)()' with an rvalue of type 'int'}}
+  int a = 1;
+  int (^z)() = a+4;   // expected-error {{cannot initialize a variable of type 'int (^)()' with an rvalue of type 'int'}}
+}
+
+int blah() {
+  int (^IFP) (float);
+  char (^PCP)(double, double, char);
+
+  IFP(1.0);
+  IFP (1.0, 2.0); // expected-error {{too many arguments to block call}}
+
+  char ch = PCP(1.0, 2.0, 'a');
+  return PCP(1.0, 2.0);   // expected-error {{too few arguments to block}}
+}
index 0108ed4f90003855f2191738eab5e4042de24627..6c2343df0e09b74d763c2ab660e3b07654f3e0ad 100644 (file)
@@ -3,7 +3,7 @@
 
 void bar(id(^)(void));
 void foo(id <NSObject>(^objectCreationBlock)(void)) {
-    return bar(objectCreationBlock); // expected-warning{{incompatible pointer types passing 'id<NSObject> (^)()' to parameter of type 'id (^)()'}}
+    return bar(objectCreationBlock); // OK
 }
 
 void bar2(id(*)(void));
@@ -104,3 +104,17 @@ namespace N3 {
     X<N> xN = ^() { return X<N>(); }();
   }
 }
+
+// rdar://8979379
+
+@interface A
+@end
+
+@interface B : A
+@end
+
+void f(int (^bl)(A* a)); // expected-note {{candidate function not viable: no known conversion from 'int (^)(B *)' to 'int (^)(A *)' for 1st argument}}
+
+void g() {
+  f(^(B* b) { return 0; }); // expected-error {{no matching function for call to 'f'}}
+}