diff options
-rw-r--r-- | clang/include/clang/AST/DeclObjC.h | 21 | ||||
-rw-r--r-- | clang/lib/AST/ASTContext.cpp | 45 | ||||
-rw-r--r-- | clang/test/CodeGenObjCXX/references.mm | 2 | ||||
-rw-r--r-- | clang/test/SemaObjC/comptypes-4.m | 2 | ||||
-rw-r--r-- | clang/test/SemaObjC/unqualified-to-qualified-class-warn.m | 31 | ||||
-rw-r--r-- | clang/test/SemaObjCXX/references.mm | 2 |
6 files changed, 96 insertions, 7 deletions
diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 84e7a63b029..241d76c4487 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 <SubFooable> + /// @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 01e8f8e3967..39229a7b731 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<P1> = lhs<P1,P2> is ok. + // But not SuperObj<P1,P2,P3> = lhs<P1,P2>. + llvm::SmallPtrSet<ObjCProtocolDecl *, 8> 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<ObjCProtocolDecl*,8>::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 8875fd62407..6265c7be761 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<P1> {} @end -@interface bar : foo <P1, P2> {} @end +@interface bar : foo <P1, P2, P3> {} @end typedef bar baz; void f5(foo&); void f5b(foo<P1>&); diff --git a/clang/test/SemaObjC/comptypes-4.m b/clang/test/SemaObjC/comptypes-4.m index 56b23b22458..adc324c91ee 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<MyProtocol> *' 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 00000000000..5bbbfd9fcc2 --- /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 <Fooable> +@end + +@interface AClass +@end + +@interface BClass : AClass <SubFooable> +@end + +@implementation BClass +- (void)foo { +} +@end + +void functionTakingAClassConformingToAProtocol(AClass <Fooable> *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<Fooable> *'}} + 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 585c75f3cdf..3a522005abe 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<P1> {} @end -@interface bar : foo <P1, P2> {} @end +@interface bar : foo <P1, P2, P3> {} @end typedef bar baz; struct ToBar { |