From c4ff5948c17bd0bd33dbca6e58370e0a5b49fb00 Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Mon, 13 Oct 2014 21:07:45 +0000 Subject: [PATCH] Objective-C [Sema]. Fixes a bug in comparing qualified Objective-C pointer types. In this case, checker incorrectly claims incompatible pointer types if redundant protocol conformance is specified. rdar://18491222 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@219630 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ASTContext.cpp | 78 ++++++++++--------------- test/SemaObjC/compare-qualified-class.m | 35 +++++++++++ test/SemaObjC/conditional-expr.m | 10 ++-- 3 files changed, 70 insertions(+), 53 deletions(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 577e82089b..09638916cf 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -6742,58 +6742,40 @@ 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, - // 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. - bool IsSuperClass = - LHS->getInterface()->isSuperClassOf(RHS->getInterface()); - if (IsSuperClass) { - // 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(RHS->getInterface(), SuperClassInheritedProtocols); - // If super class has no protocols, it is not a match. - if (SuperClassInheritedProtocols.empty()) - return false; + // Okay, we know the LHS has protocol qualifiers. But RHS may or may not. + // More detailed analysis is required. + // OK, if LHS is same or a superclass of RHS *and* + // this LHS, or as RHS's super class is assignment compatible with LHS. + bool IsSuperClass = + LHS->getInterface()->isSuperClassOf(RHS->getInterface()); + if (IsSuperClass) { + // 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(RHS->getInterface(), SuperClassInheritedProtocols); + // Also, if RHS has explicit quelifiers, include them for comparing with LHS's + // qualifiers. + for (auto *RHSPI : RHS->quals()) + SuperClassInheritedProtocols.insert(RHSPI->getCanonicalDecl()); + // If there is no protocols associated with RHS, it is not a match. + if (SuperClassInheritedProtocols.empty()) + return false; - for (const auto *LHSProto : LHS->quals()) { - bool SuperImplementsProtocol = false; - for (auto *SuperClassProto : SuperClassInheritedProtocols) { - if (SuperClassProto->lookupProtocolNamed(LHSProto->getIdentifier())) { - SuperImplementsProtocol = true; - break; - } + for (const auto *LHSProto : LHS->quals()) { + bool SuperImplementsProtocol = false; + for (auto *SuperClassProto : SuperClassInheritedProtocols) + if (SuperClassProto->lookupProtocolNamed(LHSProto->getIdentifier())) { + SuperImplementsProtocol = true; + break; } - if (!SuperImplementsProtocol) - return false; - } - return true; - } - return false; - } - - for (const auto *LHSPI : LHS->quals()) { - bool RHSImplementsProtocol = false; - - // If the RHS doesn't implement the protocol on the left, the types - // are incompatible. - for (auto *RHSPI : RHS->quals()) { - if (RHSPI->lookupProtocolNamed(LHSPI->getIdentifier())) { - RHSImplementsProtocol = true; - break; - } + if (!SuperImplementsProtocol) + return false; } - // FIXME: For better diagnostics, consider passing back the protocol name. - if (!RHSImplementsProtocol) - return false; + return true; } - // The RHS implements all protocols listed on the LHS. - return true; + return false; } bool ASTContext::areComparableObjCPointerTypes(QualType LHS, QualType RHS) { diff --git a/test/SemaObjC/compare-qualified-class.m b/test/SemaObjC/compare-qualified-class.m index 60ef851e1f..a525615214 100644 --- a/test/SemaObjC/compare-qualified-class.m +++ b/test/SemaObjC/compare-qualified-class.m @@ -28,3 +28,38 @@ int main () { classA == classD; // expected-warning {{comparison of distinct pointer types ('Class' and 'Class')}} } +// rdar://18491222 +@protocol NSObject @end + +@interface NSObject @end +@protocol ProtocolX +@end + +@protocol ProtocolY +@end + +@interface ClassA : NSObject +@end + +@interface ClassB : ClassA +@end + +@interface OtherClass : NSObject +@property (nonatomic, copy) ClassB *aProperty; +- (ClassA *)aMethod; +- (ClassA *)anotherMethod; +@end + +@implementation OtherClass +- (ClassA *)aMethod { + // This does not work, even though ClassB subclasses from A and conforms to Y + // because the property type explicity adds ProtocolX conformance + // even though ClassB already conforms to ProtocolX + return self.aProperty; +} +- (ClassA *)anotherMethod { + // This works, even though all it is doing is removing an explicit + // protocol conformance that ClassB already conforms to + return (ClassB *)self.aProperty; +} +@end diff --git a/test/SemaObjC/conditional-expr.m b/test/SemaObjC/conditional-expr.m index d8862c584a..71e108cce6 100644 --- a/test/SemaObjC/conditional-expr.m +++ b/test/SemaObjC/conditional-expr.m @@ -101,10 +101,10 @@ int f8(int a, A *x, A *y) { } void f9(int a, A *x, A *y) { - id l0 = (a ? x : y ); // expected-warning {{incompatible operand types ('A *' and 'A *')}} - A *l1 = (a ? x : y ); // expected-warning {{incompatible operand types ('A *' and 'A *')}} - A *l2 = (a ? x : y ); // expected-warning {{incompatible operand types ('A *' and 'A *')}} - [ (a ? x : y ) intProp ]; // expected-warning {{incompatible operand types ('A *' and 'A *')}} + id l0 = (a ? x : y ); // Ok. y is of A object type and A is qualified by P0. + A *l1 = (a ? x : y ); // Ok. y is of A object type and A is qualified by P0. + A *l2 = (a ? x : y ); // expected-warning {{incompatible pointer types initializing 'A *' with an expression of type 'A *'}} + (void)[ (a ? x : y ) intProp ]; // Ok. Common type is A * and P0's property intProp is accessed. } void f10(int a, id x, id y) { @@ -116,5 +116,5 @@ void f11(int a, id x, id y) { } void f12(int a, A *x, A *y) { - A* l0 = (a ? x : y ); // expected-warning {{incompatible operand types ('A *' and 'A *')}} + A* l0 = (a ? x : y ); // expected-warning {{incompatible pointer types initializing 'A *' with an expression of type 'A *'}} } -- 2.40.0