[cfe-commits] r98696 - in /cfe/trunk: include/clang/AST/ASTContext.h lib/AST/ASTContext.cpp lib/Sema/SemaExpr.cpp test/SemaObjC/block-type-safety.m

Fariborz Jahanian fjahanian at apple.com
Tue Mar 16 17:20:01 PDT 2010


Author: fjahanian
Date: Tue Mar 16 19:20:01 2010
New Revision: 98696

URL: http://llvm.org/viewvc/llvm-project?rev=98696&view=rev
Log:
objective-c patch to provide type safty when blocks are passing or
returning objc objects. There will be a corresponding objective-c++
patch soon.


Added:
    cfe/trunk/test/SemaObjC/block-type-safety.m
Modified:
    cfe/trunk/include/clang/AST/ASTContext.h
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=98696&r1=98695&r2=98696&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Tue Mar 16 19:20:01 2010
@@ -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)

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=98696&r1=98695&r2=98696&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Tue Mar 16 19:20:01 2010
@@ -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();

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=98696&r1=98695&r2=98696&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Mar 16 19:20:01 2010
@@ -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;
 }

Added: cfe/trunk/test/SemaObjC/block-type-safety.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/block-type-safety.m?rev=98696&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjC/block-type-safety.m (added)
+++ cfe/trunk/test/SemaObjC/block-type-safety.m Tue Mar 16 19:20:01 2010
@@ -0,0 +1,96 @@
+// RUN: %clang_cc1 -fsyntax-only %s -verify -fblocks
+// test for block type safety.
+
+ at interface Super  @end
+ at 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();
+}
+
+ at 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; });
+}
+
+
+ at interface A @end
+ at 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
+}
+
+ at interface NSArray
+   // Calls block() with every object in the array
+   -enumerateObjectsWithBlock:(void (^)(id obj))block;
+ at end
+
+ at interface MyThing
+-(void) printThing;
+ at end
+
+ at 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];
+       }];
+   }
+ at end
+





More information about the cfe-commits mailing list