зеркало из https://github.com/microsoft/clang-1.git
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
This commit is contained in:
Родитель
87bac6f3aa
Коммит
132f2a2da3
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
Загрузка…
Ссылка в новой задаче