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
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index d12a182..efd70ba 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -1161,6 +1161,8 @@
/// 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 @@
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 8230cde..2cf8ce7 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -4315,6 +4315,41 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 d80b25d..a4846a9 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -4579,7 +4579,11 @@
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 0000000..dab0af4
--- /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
+