diff options
author | Fariborz Jahanian <fjahanian@apple.com> | 2010-03-17 00:20:01 +0000 |
---|---|---|
committer | Fariborz Jahanian <fjahanian@apple.com> | 2010-03-17 00:20:01 +0000 |
commit | 132f2a2da34f378fc675b9e174564b0f52c31d98 (patch) | |
tree | 1cf59a4c1904d8fc061115921eee27a336037f42 | |
parent | 87bac6f3aaff2948d56ae2150396f1a880756746 (diff) |
objective-c patch to provide type safty when blocks are passing or
returning objc objects. There will be a corresponding objective-c++
patch soon.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@98696 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/ASTContext.h | 9 | ||||
-rw-r--r-- | lib/AST/ASTContext.cpp | 66 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 6 | ||||
-rw-r--r-- | test/SemaObjC/block-type-safety.m | 96 |
4 files changed, 167 insertions, 10 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index d12a182078..efd70badc9 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1161,6 +1161,8 @@ public: /// Compatibility predicates used to check assignment expressions. bool typesAreCompatible(QualType, QualType); // C99 6.2.7p1 + bool typesAreBlockPointerCompatible(QualType, QualType); + bool isObjCIdType(QualType T) const { return T == ObjCIdTypedefType; } @@ -1179,13 +1181,16 @@ public: const ObjCObjectPointerType *RHSOPT); bool canAssignObjCInterfaces(const ObjCInterfaceType *LHS, const ObjCInterfaceType *RHS); + bool canAssignObjCInterfacesInBlockPointer( + const ObjCObjectPointerType *LHSOPT, + const ObjCObjectPointerType *RHSOPT); bool areComparableObjCPointerTypes(QualType LHS, QualType RHS); QualType areCommonBaseCompatible(const ObjCObjectPointerType *LHSOPT, const ObjCObjectPointerType *RHSOPT); // Functions for calculating composite types - QualType mergeTypes(QualType, QualType); - QualType mergeFunctionTypes(QualType, QualType); + QualType mergeTypes(QualType, QualType, bool OfBlockPointer=false); + QualType mergeFunctionTypes(QualType, QualType, bool OfBlockPointer=false); /// UsualArithmeticConversionsType - handles the various conversions /// that are common to binary operators (C99 6.3.1.8, C++ [expr]p9) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 8230cde3b2..2cf8ce7aa5 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4315,6 +4315,41 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT, return false; } +/// canAssignObjCInterfacesInBlockPointer - This routine is specifically written +/// for providing type-safty for objective-c pointers used to pass/return +/// arguments in block literals. When passed as arguments, passing 'A*' where +/// 'id' is expected is not OK. Passing 'Sub *" where 'Super *" is expected is +/// not OK. For the return type, the opposite is not OK. +bool ASTContext::canAssignObjCInterfacesInBlockPointer( + const ObjCObjectPointerType *LHSOPT, + const ObjCObjectPointerType *RHSOPT) { + if (RHSOPT->isObjCBuiltinType()) + return true; + + if (LHSOPT->isObjCBuiltinType()) { + return RHSOPT->isObjCBuiltinType() || RHSOPT->isObjCQualifiedIdType(); + } + + if (LHSOPT->isObjCQualifiedIdType() || RHSOPT->isObjCQualifiedIdType()) + return ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0), + QualType(RHSOPT,0), + false); + + const ObjCInterfaceType* LHS = LHSOPT->getInterfaceType(); + const ObjCInterfaceType* RHS = RHSOPT->getInterfaceType(); + if (LHS && RHS) { // We have 2 user-defined types. + if (LHS != RHS) { + if (LHS->getDecl()->isSuperClassOf(RHS->getDecl())) + return false; + if (RHS->getDecl()->isSuperClassOf(LHS->getDecl())) + return true; + } + else + return true; + } + return false; +} + /// getIntersectionOfProtocols - This routine finds the intersection of set /// of protocols inherited from two distinct objective-c pointer objects. /// It is used to build composite qualifier list of the composite type of @@ -4451,7 +4486,12 @@ bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS) { return !mergeTypes(LHS, RHS).isNull(); } -QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs) { +bool ASTContext::typesAreBlockPointerCompatible(QualType LHS, QualType RHS) { + return !mergeTypes(LHS, RHS, true).isNull(); +} + +QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, + bool OfBlockPointer) { const FunctionType *lbase = lhs->getAs<FunctionType>(); const FunctionType *rbase = rhs->getAs<FunctionType>(); const FunctionProtoType *lproto = dyn_cast<FunctionProtoType>(lbase); @@ -4460,7 +4500,11 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs) { bool allRTypes = true; // Check return type - QualType retType = mergeTypes(lbase->getResultType(), rbase->getResultType()); + QualType retType; + if (OfBlockPointer) + retType = mergeTypes(rbase->getResultType(), lbase->getResultType(), true); + else + retType = mergeTypes(lbase->getResultType(), rbase->getResultType()); if (retType.isNull()) return QualType(); if (getCanonicalType(retType) != getCanonicalType(lbase->getResultType())) allLTypes = false; @@ -4500,7 +4544,7 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs) { for (unsigned i = 0; i < lproto_nargs; i++) { QualType largtype = lproto->getArgType(i).getUnqualifiedType(); QualType rargtype = rproto->getArgType(i).getUnqualifiedType(); - QualType argtype = mergeTypes(largtype, rargtype); + QualType argtype = mergeTypes(largtype, rargtype, OfBlockPointer); if (argtype.isNull()) return QualType(); types.push_back(argtype); if (getCanonicalType(argtype) != getCanonicalType(largtype)) @@ -4554,7 +4598,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs) { return getFunctionNoProtoType(retType, NoReturn, lcc); } -QualType ASTContext::mergeTypes(QualType LHS, QualType RHS) { +QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, + bool OfBlockPointer) { // C++ [expr]: If an expression initially has the type "reference to T", the // type is adjusted to "T" prior to any further analysis, the expression // designates the object or function denoted by the reference, and the @@ -4681,7 +4726,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS) { // Merge two block pointer types, while trying to preserve typedef info QualType LHSPointee = LHS->getAs<BlockPointerType>()->getPointeeType(); QualType RHSPointee = RHS->getAs<BlockPointerType>()->getPointeeType(); - QualType ResultType = mergeTypes(LHSPointee, RHSPointee); + QualType ResultType = mergeTypes(LHSPointee, RHSPointee, OfBlockPointer); if (ResultType.isNull()) return QualType(); if (getCanonicalType(LHSPointee) == getCanonicalType(ResultType)) return LHS; @@ -4732,7 +4777,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS) { ArrayType::ArraySizeModifier(), 0); } case Type::FunctionNoProto: - return mergeFunctionTypes(LHS, RHS); + return mergeFunctionTypes(LHS, RHS, OfBlockPointer); case Type::Record: case Type::Enum: return QualType(); @@ -4761,12 +4806,19 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS) { return QualType(); } case Type::ObjCObjectPointer: { + if (OfBlockPointer) { + if (canAssignObjCInterfacesInBlockPointer( + LHS->getAs<ObjCObjectPointerType>(), + RHS->getAs<ObjCObjectPointerType>())) + return LHS; + return QualType(); + } if (canAssignObjCInterfaces(LHS->getAs<ObjCObjectPointerType>(), RHS->getAs<ObjCObjectPointerType>())) return LHS; return QualType(); - } + } } return QualType(); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d80b25d332..a4846a9463 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -4579,7 +4579,11 @@ Sema::CheckBlockPointerTypesForAssignment(QualType lhsType, if (lhptee.getLocalCVRQualifiers() != rhptee.getLocalCVRQualifiers()) ConvTy = CompatiblePointerDiscardsQualifiers; - if (!Context.typesAreCompatible(lhptee, rhptee)) + if (!getLangOptions().CPlusPlus) { + if (!Context.typesAreBlockPointerCompatible(lhsType, rhsType)) + return IncompatibleBlockPointer; + } + else if (!Context.typesAreCompatible(lhptee, rhptee)) return IncompatibleBlockPointer; return ConvTy; } diff --git a/test/SemaObjC/block-type-safety.m b/test/SemaObjC/block-type-safety.m new file mode 100644 index 0000000000..dab0af4026 --- /dev/null +++ b/test/SemaObjC/block-type-safety.m @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -fsyntax-only %s -verify -fblocks +// test for block type safety. + +@interface Super @end +@interface Sub : Super @end + +void f2(void(^f)(Super *)) { + Super *o; + f(o); +} + +void f3(void(^f)(Sub *)) { + Sub *o; + f(o); +} + +void r0(Super* (^f)()) { + Super *o = f(); +} + +void r1(Sub* (^f)()) { + Sub *o = f(); +} + +@protocol NSObject; + +void r2 (id<NSObject> (^f) (void)) { + id o = f(); +} + +void test1() { + f2(^(Sub *o) { }); // expected-error {{incompatible block pointer types passing 'void (^)(Sub *)', expected 'void (^)(Super *)'}} + f3(^(Super *o) { }); // OK, block taking Super* may be called with a Sub* + + r0(^Super* () { return 0; }); // OK + r0(^Sub* () { return 0; }); // OK, variable of type Super* gets return value of type Sub* + r0(^id () { return 0; }); // expected-error {{incompatible block pointer types passing 'id (^)(void)', expected 'Super *(^)()'}} + + r1(^Super* () { return 0; }); // expected-error {{incompatible block pointer types passing 'Super *(^)(void)', expected 'Sub *(^)()'}} + r1(^Sub* () { return 0; }); // OK + r1(^id () { return 0; }); // expected-error {{incompatible block pointer types passing 'id (^)(void)', expected 'Sub *(^)()'}} + + r2(^id<NSObject>() { return 0; }); +} + + +@interface A @end +@interface B @end + +void f0(void (^f)(A* x)) { + A* a; + f(a); +} + +void f1(void (^f)(id x)) { + B *b; + f(b); +} + +void test2(void) +{ + f0(^(id a) { }); // OK + f1(^(A* a) { }); // expected-error {{incompatible block pointer types passing 'void (^)(A *)', expected 'void (^)(id)'}} + f1(^(id<NSObject> a) { }); // OK +} + +@interface NSArray + // Calls block() with every object in the array + -enumerateObjectsWithBlock:(void (^)(id obj))block; +@end + +@interface MyThing +-(void) printThing; +@end + +@implementation MyThing + static NSArray* myThings; // array of MyThing* + + -(void) printThing { } + +// programmer wants to write this: + -printMyThings1 { + [myThings enumerateObjectsWithBlock: ^(MyThing *obj) { // expected-error {{incompatible block pointer types sending 'void (^)(MyThing *)', expected 'void (^)(id)'}} + [obj printThing]; + }]; + } + +// strict type safety requires this: + -printMyThings { + [myThings enumerateObjectsWithBlock: ^(id obj) { + MyThing *obj2 = (MyThing *)obj; + [obj2 printThing]; + }]; + } +@end + |