objc - Treat type of 'self' in class methods as root of
class of this method. // rdar://10109725
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@139989 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index e5afd2d..6faf93f 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -675,8 +675,9 @@
GlobalMethodPool::iterator ReadMethodPool(Selector Sel);
- /// Private Helper predicate to check for 'self'.
- bool isSelfExpr(Expr *RExpr);
+ /// Private Helper predicate to check for 'self'. Upon success, it
+ /// returns method declaration where 'self' is referenced.
+ const ObjCMethodDecl *GetMethodIfSelfExpr(Expr *RExpr);
public:
Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
TranslationUnitKind TUKind = TU_Complete,
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 2d80ac1..715a4bd 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -5074,6 +5074,23 @@
OK));
}
+/// SelfInClassMethodType - convet type of 'self' in class method
+/// to pointer to root of method's class.
+static void
+SelfInClassMethodType(Sema &S, Expr *selfExpr, QualType &SelfType) {
+ if (const ObjCMethodDecl *MD = S.GetMethodIfSelfExpr(selfExpr))
+ if (MD->isClassMethod()) {
+ const ObjCInterfaceDecl *Root = 0;
+ if (const ObjCInterfaceDecl * IDecl = MD->getClassInterface())
+ do {
+ Root = IDecl;
+ } while ((IDecl = IDecl->getSuperClass()));
+ if (Root)
+ SelfType = S.Context.getObjCObjectPointerType(
+ S.Context.getObjCInterfaceType(Root));
+ }
+}
+
// checkPointerTypesForAssignment - This is a very tricky routine (despite
// being closely modeled after the C99 spec:-). The odd characteristic of this
// routine is it effectively iqnores the qualifiers on the top level pointee.
@@ -5309,6 +5326,8 @@
return Compatible;
}
+ SelfInClassMethodType(*this, RHS.get(), RHSType);
+
// If the left-hand side is a reference type, then we are in a
// (rare!) case where we've allowed the use of references in C,
// e.g., as a parameter type in a built-in function. In this case,
@@ -9564,7 +9583,7 @@
Selector Sel = ME->getSelector();
// self = [<foo> init...]
- if (isSelfExpr(Op->getLHS()) && Sel.getNameForSlot(0).startswith("init"))
+ if (GetMethodIfSelfExpr(Op->getLHS()) && Sel.getNameForSlot(0).startswith("init"))
diagnostic = diag::warn_condition_is_idiomatic_assignment;
// <foo> = [<bar> nextObject]
diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp
index 51b5e4f..5858596 100644
--- a/lib/Sema/SemaExprObjC.cpp
+++ b/lib/Sema/SemaExprObjC.cpp
@@ -457,18 +457,20 @@
return IsError;
}
-bool Sema::isSelfExpr(Expr *receiver) {
+/// GetMethodIfSelfExpr - Check if receiver is an objc 'self' expression
+/// and return its method declaration if so; else return 0.
+const ObjCMethodDecl *Sema::GetMethodIfSelfExpr(Expr *receiver) {
// 'self' is objc 'self' in an objc method only.
DeclContext *DC = CurContext;
while (isa<BlockDecl>(DC))
DC = DC->getParent();
if (DC && !isa<ObjCMethodDecl>(DC))
- return false;
+ return 0;
receiver = receiver->IgnoreParenLValueCasts();
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(receiver))
if (DRE->getDecl()->getIdentifier() == &Context.Idents.get("self"))
- return true;
- return false;
+ return static_cast<ObjCMethodDecl*>(DC);
+ return 0;
}
// Helper method for ActOnClassMethod/ActOnInstanceMethod.
@@ -1272,7 +1274,7 @@
}
if (!Method) {
// If not messaging 'self', look for any factory method named 'Sel'.
- if (!Receiver || !isSelfExpr(Receiver)) {
+ if (!Receiver || !GetMethodIfSelfExpr(Receiver)) {
Method = LookupFactoryMethodInGlobalPool(Sel,
SourceRange(LBracLoc, RBracLoc),
true);
@@ -1336,7 +1338,7 @@
return ExprError();
}
- if (!Method && (!Receiver || !isSelfExpr(Receiver))) {
+ if (!Method && (!Receiver || !GetMethodIfSelfExpr(Receiver))) {
// If we still haven't found a method, look in the global pool. This
// behavior isn't very desirable, however we need it for GCC
// compatibility. FIXME: should we deviate??
@@ -1507,7 +1509,7 @@
if (getLangOptions().ObjCAutoRefCount) {
// In ARC, annotate delegate init calls.
if (Result->getMethodFamily() == OMF_init &&
- (SuperLoc.isValid() || isSelfExpr(Receiver))) {
+ (SuperLoc.isValid() || GetMethodIfSelfExpr(Receiver))) {
// Only consider init calls *directly* in init implementations,
// not within blocks.
ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(CurContext);
diff --git a/test/SemaObjC/class-type-conversion.m b/test/SemaObjC/class-type-conversion.m
new file mode 100644
index 0000000..9d369fa
--- /dev/null
+++ b/test/SemaObjC/class-type-conversion.m
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// rdar://10109725
+
+@interface NSObject {
+ Class isa;
+}
+- (id)addObserver:(NSObject *)observer; // expected-note 2 {{passing argument to parameter 'observer' here}}
+@end
+
+@interface MyClass : NSObject {
+}
+@end
+
+@implementation NSObject
++ (void)initialize
+{
+ NSObject *obj = 0;
+ [obj addObserver:self];
+}
+
+- init
+{
+ NSObject *obj = 0;
+ [obj addObserver:self];
+ return [obj addObserver:(Class)0]; // expected-warning {{incompatible pointer types sending 'Class' to parameter of type 'NSObject *'}}
+}
+- (id)addObserver:(NSObject *)observer { return 0; }
+@end
+
+@implementation MyClass
+
++ (void)initialize
+{
+ NSObject *obj = 0;
+ [obj addObserver:self];
+}
+
+- init
+{
+ NSObject *obj = 0;
+ [obj addObserver:self];
+ return [obj addObserver:(Class)0]; // expected-warning {{incompatible pointer types sending 'Class' to parameter of type 'NSObject *'}}
+}
+@end