diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 84e7a63b0299..241d76c44872 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -629,6 +629,27 @@ public: return false; } + /// getImmSubClassOf - Returns Immediate sub-class of the specified interface class + /// if 'Super' is a superclass of this class. null if no such super class. + /// So in this example if 'this' is 'BClass' and 'Super' is 'AClass' then 'BClass' + /// is returned. + /// \code + /// @interface BClass : AClass + /// @end + /// \endcode + + ObjCInterfaceDecl *getImmSubClassOf(const ObjCInterfaceDecl *Super) { + ObjCInterfaceDecl *ImmSubClass = this; + ObjCInterfaceDecl *I = this->getSuperClass(); + while (I != NULL) { + if (Super == I) + return ImmSubClass; + ImmSubClass = I; + I = I->getSuperClass(); + } + return NULL; + } + ObjCIvarDecl *lookupInstanceVariable(IdentifierInfo *IVarName, ObjCInterfaceDecl *&ClassDeclared); ObjCIvarDecl *lookupInstanceVariable(IdentifierInfo *IVarName) { diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 01e8f8e39676..39229a7b731e 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4981,10 +4981,47 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectType *LHS, if (LHS->getNumProtocols() == 0) return true; - // Okay, we know the LHS has protocol qualifiers. If the RHS doesn't, then it - // isn't a superset. - if (RHS->getNumProtocols() == 0) - return true; // FIXME: should return false! + // Okay, we know the LHS has protocol qualifiers. If the RHS doesn't, + // more detailed analysis is required. + if (RHS->getNumProtocols() == 0) { + // OK, if LHS is a superclass of RHS *and* + // this superclass is assignment compatible with LHS. + // false otherwise. + ObjCInterfaceDecl *SuperClass = + RHS->getInterface()->getImmSubClassOf(LHS->getInterface()); + if (SuperClass) { + // OK if conversion of LHS to SuperClass results in narrowing of types + // ; i.e., SuperClass may implement at least one of the protocols + // in LHS's protocol list. Example, SuperObj = lhs is ok. + // But not SuperObj = lhs. + llvm::SmallPtrSet SuperClassInheritedProtocols; + CollectInheritedProtocols(SuperClass, SuperClassInheritedProtocols); + // If super class has no protocols, it is not a match. + if (SuperClassInheritedProtocols.empty()) + return false; + + for (ObjCObjectType::qual_iterator LHSPI = LHS->qual_begin(), + LHSPE = LHS->qual_end(); + LHSPI != LHSPE; LHSPI++) { + bool SuperImplementsProtocol = false; + ObjCProtocolDecl *LHSProto = (*LHSPI); + + for (llvm::SmallPtrSet::iterator I = + SuperClassInheritedProtocols.begin(), + E = SuperClassInheritedProtocols.end(); I != E; ++I) { + ObjCProtocolDecl *SuperClassProto = (*I); + if (SuperClassProto->lookupProtocolNamed(LHSProto->getIdentifier())) { + SuperImplementsProtocol = true; + break; + } + } + if (!SuperImplementsProtocol) + return false; + } + return true; + } + return false; + } for (ObjCObjectType::qual_iterator LHSPI = LHS->qual_begin(), LHSPE = LHS->qual_end(); diff --git a/clang/test/CodeGenObjCXX/references.mm b/clang/test/CodeGenObjCXX/references.mm index 8875fd624074..6265c7be7613 100644 --- a/clang/test/CodeGenObjCXX/references.mm +++ b/clang/test/CodeGenObjCXX/references.mm @@ -30,7 +30,7 @@ void f(B* b) { @protocol P2 @end @protocol P3 @end @interface foo {} @end -@interface bar : foo {} @end +@interface bar : foo {} @end typedef bar baz; void f5(foo&); void f5b(foo&); diff --git a/clang/test/SemaObjC/comptypes-4.m b/clang/test/SemaObjC/comptypes-4.m index 56b23b22458f..adc324c91ee5 100644 --- a/clang/test/SemaObjC/comptypes-4.m +++ b/clang/test/SemaObjC/comptypes-4.m @@ -12,7 +12,7 @@ int main() MyClass *obj_cp; obj_cp = obj_p; - obj_p = obj_cp; + obj_p = obj_cp; // expected-warning {{incompatible pointer types assigning to 'MyClass *' from 'MyClass *'}} if (obj_cp == obj_p) foo(); diff --git a/clang/test/SemaObjC/unqualified-to-qualified-class-warn.m b/clang/test/SemaObjC/unqualified-to-qualified-class-warn.m new file mode 100644 index 000000000000..5bbbfd9fcc25 --- /dev/null +++ b/clang/test/SemaObjC/unqualified-to-qualified-class-warn.m @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// rdar://9091389 + +@protocol Fooable +- (void)foo; +@end + +@protocol SubFooable +@end + +@interface AClass +@end + +@interface BClass : AClass +@end + +@implementation BClass +- (void)foo { +} +@end + +void functionTakingAClassConformingToAProtocol(AClass *instance) { // expected-note {{passing argument to parameter 'instance' here}} +} + +int main () { + AClass *aobject = 0; + BClass *bobject = 0; + functionTakingAClassConformingToAProtocol(aobject); // expected-warning {{incompatible pointer types passing 'AClass *' to parameter of type 'AClass *'}} + functionTakingAClassConformingToAProtocol(bobject); // Shouldn't warn - does implement Fooable + return 0; +} diff --git a/clang/test/SemaObjCXX/references.mm b/clang/test/SemaObjCXX/references.mm index 585c75f3cdf9..3a522005abee 100644 --- a/clang/test/SemaObjCXX/references.mm +++ b/clang/test/SemaObjCXX/references.mm @@ -37,7 +37,7 @@ void f4(NSString &tmpstr) { @protocol P2 @end @protocol P3 @end @interface foo {} @end -@interface bar : foo {} @end +@interface bar : foo {} @end typedef bar baz; struct ToBar {