arphaman created this revision. Prior to this patch, messages to `self` in class methods were treated as instance methods to a `Class` value. When these methods returned `instancetype` the compiler only saw `id` through the `instancetype`, and not the `Interface *`. This caused problems when that return value was a receiver in a message send, as the compiler couldn't select the right method declaration and had to rely on a selection from the global method pool.
This patch modifies the semantics of such message sends and uses class messages that are dispatched to the interface that corresponds to the class that contains the class method. This ensures that `instancetype`s are correctly interpreted by the compiler. This is an ARC only change, since non-ARC code can reassign `self`. The type `dectltype(self)` is used to store the `self` receiver in the class message to ensure that `self` remains in the AST. Thanks for taking a look. rdar://20940997 Repository: rL LLVM https://reviews.llvm.org/D36790 Files: lib/Sema/SemaExprObjC.cpp test/SemaObjC/multiple-method-names-class-self.m Index: test/SemaObjC/multiple-method-names-class-self.m =================================================================== --- /dev/null +++ test/SemaObjC/multiple-method-names-class-self.m @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -Wobjc-multiple-method-names -x objective-c -verify %s +// RUN: %clang_cc1 -Wobjc-multiple-method-names -x objective-c -verify -fobjc-arc -DARC %s +#ifdef ARC +// expected-no-diagnostics +#endif + +@interface NSObj + ++ (instancetype) alloc; + +@end + +@interface SelfAllocReturn: NSObj + +- (instancetype)initWithFoo:(int)x; +#ifndef ARC +// expected-note@-2 {{using}} +#endif + +@end + +@interface SelfAllocReturn2: NSObj + +- (instancetype)initWithFoo:(SelfAllocReturn *)x; +#ifndef ARC +// expected-note@-2 {{also found}} +#endif + +@end + +@implementation SelfAllocReturn + +- (instancetype)initWithFoo:(int)x { + return self; +} + ++ (instancetype) thingWithFoo:(int)x { + return [[self alloc] initWithFoo: x]; +#ifndef ARC +// expected-warning@-2 {{multiple methods named 'initWithFoo:' found}} +#endif +} + +@end Index: lib/Sema/SemaExprObjC.cpp =================================================================== --- lib/Sema/SemaExprObjC.cpp +++ lib/Sema/SemaExprObjC.cpp @@ -1320,9 +1320,11 @@ } } - // - if the receiver is the name of a class U, T is a pointer to U + // - if the receiver is the name of a class U, T is a pointer to U. U should + // be desugared to avoid 'decltype(self)' propagation. if (ReceiverType->getAsObjCInterfaceType()) - return transferNullability(Context.getObjCObjectPointerType(ReceiverType)); + return transferNullability(Context.getObjCObjectPointerType( + ReceiverType.getDesugaredType(Context))); // - if the receiver is of type Class or qualified Class type, // T is the declared return type of the method. if (ReceiverType->isObjCClassType() || @@ -2738,10 +2740,31 @@ } else if (ReceiverType->isObjCClassOrClassKindOfType() || ReceiverType->isObjCQualifiedClassType()) { // Handle messages to Class. + // Treat messages to 'self' in class methods as class messages when ARC + // is enabled (because self can't be reassigned when ARC is on). + if (Receiver->isObjCSelfExpr() && getLangOpts().ObjCAutoRefCount) { + assert(ReceiverType->isObjCClassType() && "expected a Class self"); + const ObjCMethodDecl *MD = cast<ObjCMethodDecl>( + cast<ImplicitParamDecl>( + cast<DeclRefExpr>(Receiver->IgnoreParenImpCasts())->getDecl()) + ->getDeclContext()); + assert(MD->isClassMethod() && "expected a class method"); + QualType ReceiverType = + Context.getObjCInterfaceType(MD->getClassInterface()); + // Use a pseudo-decltype type to keep the expression in the AST. + assert(!Receiver->isInstantiationDependent() && + "unexpected dependent expr"); + ReceiverType = Context.getDecltypeType(Receiver, ReceiverType); + return BuildClassMessage( + Context.getTrivialTypeSourceInfo(ReceiverType, LBracLoc), + ReceiverType, + /*SuperLoc=*/SourceLocation(), Sel, + /*Method=*/nullptr, LBracLoc, SelectorLocs, RBracLoc, ArgsIn); + } // We allow sending a message to a qualified Class ("Class<foo>"), which // is ok as long as one of the protocols implements the selector (if not, // warn). - if (!ReceiverType->isObjCClassOrClassKindOfType()) { + else if (!ReceiverType->isObjCClassOrClassKindOfType()) { const ObjCObjectPointerType *QClassTy = ReceiverType->getAsObjCQualifiedClassType(); // Search protocols for class methods.
Index: test/SemaObjC/multiple-method-names-class-self.m =================================================================== --- /dev/null +++ test/SemaObjC/multiple-method-names-class-self.m @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -Wobjc-multiple-method-names -x objective-c -verify %s +// RUN: %clang_cc1 -Wobjc-multiple-method-names -x objective-c -verify -fobjc-arc -DARC %s +#ifdef ARC +// expected-no-diagnostics +#endif + +@interface NSObj + ++ (instancetype) alloc; + +@end + +@interface SelfAllocReturn: NSObj + +- (instancetype)initWithFoo:(int)x; +#ifndef ARC +// expected-note@-2 {{using}} +#endif + +@end + +@interface SelfAllocReturn2: NSObj + +- (instancetype)initWithFoo:(SelfAllocReturn *)x; +#ifndef ARC +// expected-note@-2 {{also found}} +#endif + +@end + +@implementation SelfAllocReturn + +- (instancetype)initWithFoo:(int)x { + return self; +} + ++ (instancetype) thingWithFoo:(int)x { + return [[self alloc] initWithFoo: x]; +#ifndef ARC +// expected-warning@-2 {{multiple methods named 'initWithFoo:' found}} +#endif +} + +@end Index: lib/Sema/SemaExprObjC.cpp =================================================================== --- lib/Sema/SemaExprObjC.cpp +++ lib/Sema/SemaExprObjC.cpp @@ -1320,9 +1320,11 @@ } } - // - if the receiver is the name of a class U, T is a pointer to U + // - if the receiver is the name of a class U, T is a pointer to U. U should + // be desugared to avoid 'decltype(self)' propagation. if (ReceiverType->getAsObjCInterfaceType()) - return transferNullability(Context.getObjCObjectPointerType(ReceiverType)); + return transferNullability(Context.getObjCObjectPointerType( + ReceiverType.getDesugaredType(Context))); // - if the receiver is of type Class or qualified Class type, // T is the declared return type of the method. if (ReceiverType->isObjCClassType() || @@ -2738,10 +2740,31 @@ } else if (ReceiverType->isObjCClassOrClassKindOfType() || ReceiverType->isObjCQualifiedClassType()) { // Handle messages to Class. + // Treat messages to 'self' in class methods as class messages when ARC + // is enabled (because self can't be reassigned when ARC is on). + if (Receiver->isObjCSelfExpr() && getLangOpts().ObjCAutoRefCount) { + assert(ReceiverType->isObjCClassType() && "expected a Class self"); + const ObjCMethodDecl *MD = cast<ObjCMethodDecl>( + cast<ImplicitParamDecl>( + cast<DeclRefExpr>(Receiver->IgnoreParenImpCasts())->getDecl()) + ->getDeclContext()); + assert(MD->isClassMethod() && "expected a class method"); + QualType ReceiverType = + Context.getObjCInterfaceType(MD->getClassInterface()); + // Use a pseudo-decltype type to keep the expression in the AST. + assert(!Receiver->isInstantiationDependent() && + "unexpected dependent expr"); + ReceiverType = Context.getDecltypeType(Receiver, ReceiverType); + return BuildClassMessage( + Context.getTrivialTypeSourceInfo(ReceiverType, LBracLoc), + ReceiverType, + /*SuperLoc=*/SourceLocation(), Sel, + /*Method=*/nullptr, LBracLoc, SelectorLocs, RBracLoc, ArgsIn); + } // We allow sending a message to a qualified Class ("Class<foo>"), which // is ok as long as one of the protocols implements the selector (if not, // warn). - if (!ReceiverType->isObjCClassOrClassKindOfType()) { + else if (!ReceiverType->isObjCClassOrClassKindOfType()) { const ObjCObjectPointerType *QClassTy = ReceiverType->getAsObjCQualifiedClassType(); // Search protocols for class methods.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits