diff options
author | Fariborz Jahanian <fjahanian@apple.com> | 2011-02-12 19:07:46 +0000 |
---|---|---|
committer | Fariborz Jahanian <fjahanian@apple.com> | 2011-02-12 19:07:46 +0000 |
commit | e3c8c64e7735c3589e1a34e6000c93183a55920c (patch) | |
tree | 9aba919860f16004c3c529a163b08c934dce0a14 | |
parent | 020c374273ab6099acbed747a7f27aebf8f0af1d (diff) |
Implement objective-c++'s block pointer type matching involving
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
-rw-r--r-- | include/clang/Sema/Overload.h | 1 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 2 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 7 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 88 | ||||
-rw-r--r-- | test/CodeGenObjCXX/blocks.mm | 30 | ||||
-rw-r--r-- | test/SemaCXX/block-call.cpp | 52 | ||||
-rw-r--r-- | test/SemaObjCXX/blocks.mm | 16 |
7 files changed, 194 insertions, 2 deletions
diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h index fb7f5f129d..3ce3513c21 100644 --- a/include/clang/Sema/Overload.h +++ b/include/clang/Sema/Overload.h @@ -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 }; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 4731ad8d27..cfd21df753 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index f886e17d54..e9f595549f 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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: diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 42e24116bf..70cc566059 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -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 index 0000000000..ffb916bf03 --- /dev/null +++ b/test/CodeGenObjCXX/blocks.mm @@ -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 index 0000000000..d519911589 --- /dev/null +++ b/test/SemaCXX/block-call.cpp @@ -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}} +} diff --git a/test/SemaObjCXX/blocks.mm b/test/SemaObjCXX/blocks.mm index 0108ed4f90..6c2343df0e 100644 --- a/test/SemaObjCXX/blocks.mm +++ b/test/SemaObjCXX/blocks.mm @@ -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'}} +} |