diff options
-rw-r--r-- | AST/ASTContext.cpp | 97 | ||||
-rw-r--r-- | test/Sema/objc-comptypes-5.m | 37 |
2 files changed, 108 insertions, 26 deletions
diff --git a/AST/ASTContext.cpp b/AST/ASTContext.cpp index cd7b3cd73b..31d9255a4b 100644 --- a/AST/ASTContext.cpp +++ b/AST/ASTContext.cpp @@ -1306,6 +1306,39 @@ static bool ProtocolCompatibleWithProtocol(ObjcProtocolDecl *lProto, return false; } +/// ClassImplementsProtocol - Checks that 'lProto' protocol +/// has been implemented in IDecl class, its super class or categories (if +/// lookupCategory is true). +static bool ClassImplementsProtocol(ObjcProtocolDecl *lProto, + ObjcInterfaceDecl *IDecl, + bool lookupCategory) { + + // 1st, look up the class. + ObjcProtocolDecl **protoList = IDecl->getReferencedProtocols(); + for (unsigned i = 0; i < IDecl->getNumIntfRefProtocols(); i++) { + if (ProtocolCompatibleWithProtocol(lProto, protoList[i])) + return true; + } + + // 2nd, look up the category. + if (lookupCategory) + for (ObjcCategoryDecl *CDecl = IDecl->getCategoryList(); CDecl; + CDecl = CDecl->getNextClassCategory()) { + protoList = CDecl->getReferencedProtocols(); + for (unsigned i = 0; i < CDecl->getNumReferencedProtocols(); i++) { + if (ProtocolCompatibleWithProtocol(lProto, protoList[i])) + return true; + } + } + + // 3rd, look up the super class(s) + if (IDecl->getSuperClass()) + return + ClassImplementsProtocol(lProto, IDecl->getSuperClass(), lookupCategory); + + return false; +} + /// ObjcQualifiedIdTypesAreCompatible - Compares two types, at least /// one of which is a protocol qualified 'id' type. When 'compare' /// is true it is for comparison; when false, for assignment/initialization. @@ -1350,24 +1383,29 @@ bool ASTContext::ObjcQualifiedIdTypesAreCompatible(QualType lhs, if (!rhsQI && !rhsQID && !rhsID) return false; + unsigned numRhsProtocols; + ObjcProtocolDecl **rhsProtoList; + if (rhsQI) { + numRhsProtocols = rhsQI->getNumProtocols(); + rhsProtoList = rhsQI->getReferencedProtocols(); + } + else if (rhsQID) { + numRhsProtocols = rhsQID->getNumProtocols(); + rhsProtoList = rhsQID->getReferencedProtocols(); + } + for (unsigned i =0; i < lhsQID->getNumProtocols(); i++) { - bool match = false; ObjcProtocolDecl *lhsProto = lhsQID->getProtocols(i); - unsigned numRhsProtocols; - ObjcProtocolDecl **rhsProtoList; - if (rhsQI) { - numRhsProtocols = rhsQI->getNumProtocols(); - rhsProtoList = rhsQI->getReferencedProtocols(); - } - else if (rhsQID) { - numRhsProtocols = rhsQID->getNumProtocols(); - rhsProtoList = rhsQID->getReferencedProtocols(); - } - else { - numRhsProtocols = rhsID->getNumIntfRefProtocols(); - rhsProtoList = rhsID->getReferencedProtocols(); + bool match = false; + + // when comparing an id<P> on lhs with a static type on rhs, + // see if static class implements all of id's protocols, directly or + // through its super class and categories. + if (rhsID) { + if (ClassImplementsProtocol(lhsProto, rhsID, true)) + match = true; } - for (unsigned j = 0; j < numRhsProtocols; j++) { + else for (unsigned j = 0; j < numRhsProtocols; j++) { ObjcProtocolDecl *rhsProto = rhsProtoList[j]; if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) || compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto)) { @@ -1405,14 +1443,21 @@ bool ASTContext::ObjcQualifiedIdTypesAreCompatible(QualType lhs, else if (lhsQID) { numLhsProtocols = lhsQID->getNumProtocols(); lhsProtoList = lhsQID->getReferencedProtocols(); - } - else { - numLhsProtocols = lhsID->getNumIntfRefProtocols(); - lhsProtoList = lhsID->getReferencedProtocols(); - } - - for (unsigned i =0; i < numLhsProtocols; i++) { - bool match = false; + } + bool match = false; + // for static type vs. qualified 'id' type, check that class implements + // one of 'id's protocols. + if (lhsID) { + for (unsigned j = 0; j < rhsQID->getNumProtocols(); j++) { + ObjcProtocolDecl *rhsProto = rhsQID->getProtocols(j); + if (ClassImplementsProtocol(rhsProto, lhsID, compare)) { + match = true; + break; + } + } + } + else for (unsigned i =0; i < numLhsProtocols; i++) { + match = false; ObjcProtocolDecl *lhsProto = lhsProtoList[i]; for (unsigned j = 0; j < rhsQID->getNumProtocols(); j++) { ObjcProtocolDecl *rhsProto = rhsQID->getProtocols(j); @@ -1422,9 +1467,9 @@ bool ASTContext::ObjcQualifiedIdTypesAreCompatible(QualType lhs, break; } } - if (!match) - return false; - } + } + if (!match) + return false; } return true; } diff --git a/test/Sema/objc-comptypes-5.m b/test/Sema/objc-comptypes-5.m new file mode 100644 index 0000000000..646c6e58bc --- /dev/null +++ b/test/Sema/objc-comptypes-5.m @@ -0,0 +1,37 @@ +// RUN: clang -fsyntax-only -verify %s + +extern void foo(); +#include <objc/objc.h> + +@protocol MyProtocol +- (void) method; +@end + +@interface MyClass +@end + +@interface MyClass (Addition) <MyProtocol> +- (void) method; +@end + +@interface MyOtherClass : MyClass +@end + +int main() +{ + id <MyProtocol> obj_id_p = nil; + MyClass *obj_c_cat_p = nil; + MyOtherClass *obj_c_super_p = nil; + + obj_c_cat_p = obj_id_p; // expected-error {{incompatible types assigning 'id<MyProtocol>' to 'MyClass *'}} + obj_c_super_p = obj_id_p; // expected-error {{incompatible types assigning 'id<MyProtocol>' to 'MyOtherClass *'}} + obj_id_p = obj_c_cat_p; /* Ok */ + obj_id_p = obj_c_super_p; /* Ok */ + + if (obj_c_cat_p == obj_id_p) foo(); /* Ok */ + if (obj_c_super_p == obj_id_p) foo() ; /* Ok */ + if (obj_id_p == obj_c_cat_p) foo(); /* Ok */ + if (obj_id_p == obj_c_super_p) foo(); /* Ok */ + + return 0; +} |