From bab8a96f2f509218bc1a25d67bc182581d65e50e Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 8 Sep 2011 01:46:34 +0000 Subject: [PATCH] Implement the Objective-C 'instancetype' type, which is an alias of 'id' that can be used (only!) via a contextual keyword as the result type of an Objective-C message send. 'instancetype' then gives the method a related result type, which we have already been inferring for a variety of methods (new, alloc, init, self, retain). Addresses . llvm-svn: 139275 --- clang/docs/LanguageExtensions.html | 18 +- clang/include/clang/AST/ASTContext.h | 13 ++ .../clang/Basic/DiagnosticSemaKinds.td | 7 +- clang/include/clang/Parse/Parser.h | 3 + clang/include/clang/Sema/Sema.h | 4 + .../include/clang/Serialization/ASTBitCodes.h | 7 +- clang/lib/AST/ASTContext.cpp | 13 +- clang/lib/Lex/PPMacroExpansion.cpp | 1 + clang/lib/Parse/ParseObjc.cpp | 10 +- clang/lib/Parse/Parser.cpp | 1 + clang/lib/Sema/SemaDeclObjC.cpp | 53 +++-- clang/lib/Sema/SemaExprObjC.cpp | 4 + clang/lib/Sema/SemaType.cpp | 7 + clang/lib/Serialization/ASTReader.cpp | 4 + clang/lib/Serialization/ASTWriter.cpp | 2 + clang/test/PCH/objc_methods.h | 2 +- clang/test/PCH/objc_methods.m | 2 +- clang/test/SemaObjC/instancetype.m | 190 ++++++++++++++++++ 18 files changed, 313 insertions(+), 28 deletions(-) create mode 100644 clang/test/SemaObjC/instancetype.m diff --git a/clang/docs/LanguageExtensions.html b/clang/docs/LanguageExtensions.html index b621eab311f6..a7d8019f975d 100644 --- a/clang/docs/LanguageExtensions.html +++ b/clang/docs/LanguageExtensions.html @@ -723,7 +723,19 @@ related result type. Similarly, the type of the expression init has a related result type and its receiver is known to have the type NSArray *. If neither alloc nor init had a related result type, the expressions would have had type id, as declared in the method signature.

-

To determine whether a method has a related result type, the first +

A method with a related result type can be declared by using the +type instancetype as its result type. instancetype +is a contextual keyword that is only permitted in the result type of +an Objective-C method, e.g.

+ +
+@interface A
++ (instancetype)constructAnA;
+@end
+
+ +

The related result type can also be inferred for some methods. +To determine whether a method has an inferred related result type, the first word in the camel-case selector (e.g., "init" in "initWithObjects") is considered, and the method will a related result type if its return type is compatible with the type of its class and if

@@ -752,8 +764,8 @@ with the subclass type. For example:

Related result types only affect the type of a message send or property access via the given method. In all other respects, a method -with a related result type is treated the same way as method without a -related result type.

+with a related result type is treated the same way as method that +returns id.

Automatic reference counting

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index e46cc6dc1fe7..e1a89b35df95 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -211,6 +211,9 @@ class ASTContext : public llvm::RefCountedBase { QualType ObjCConstantStringType; mutable RecordDecl *CFConstantStringTypeDecl; + /// \brief The typedef declaration for the Objective-C "instancetype" type. + TypedefDecl *ObjCInstanceTypeDecl; + /// \brief The type for the C FILE type. TypeDecl *FILEDecl; @@ -884,6 +887,16 @@ public: ObjCSelRedefinitionType = RedefType; } + /// \brief Retrieve the Objective-C "instancetype" type, if already known; + /// otherwise, returns a NULL type; + QualType getObjCInstanceType() { + return getTypeDeclType(getObjCInstanceTypeDecl()); + } + + /// \brief Retrieve the typedef declaration corresponding to the Objective-C + /// "instancetype" type. + TypedefDecl *getObjCInstanceTypeDecl(); + /// \brief Set the type for the C FILE type. void setFILEDecl(TypeDecl *FILEDecl) { this->FILEDecl = FILEDecl; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a90849cc1969..933632392f5d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4696,9 +4696,12 @@ def warn_related_result_type_compatibility_class : Warning< def warn_related_result_type_compatibility_protocol : Warning< "protocol method is expected to return an instance of the implementing " "class, but is declared to return %0">; -def note_related_result_type_overridden : Note< +def note_related_result_type_overridden_family : Note< "overridden method is part of the '%select{|alloc|copy|init|mutableCopy|" - "new|autorelease|dealloc|release|retain|retainCount|self}0' method family">; + "new|autorelease|dealloc|finalize|release|retain|retainCount|self}0' method " + "family">; +def note_related_result_type_overridden : Note< + "overridden method returns an instance of its class type">; def note_related_result_type_inferred : Note< "%select{class|instance}0 method %1 is assumed to return an instance of " "its receiver type (%2)">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index c9ba56e6b7ca..1b0949c3242c 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -120,6 +120,9 @@ class Parser : public CodeCompletionHandler { IdentifierInfo *Ident_vector; IdentifierInfo *Ident_pixel; + /// Objective-C contextual keywords. + mutable IdentifierInfo *Ident_instancetype; + /// \brief Identifier for "introduced". IdentifierInfo *Ident_introduced; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 423f29f27885..cfc267c1cffb 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -832,6 +832,10 @@ public: TypeResult ActOnTypeName(Scope *S, Declarator &D); + /// \brief The parser has parsed the context-sensitive type 'instancetype' + /// in an Objective-C message declaration. Return the appropriate type. + ParsedType ActOnObjCInstanceType(SourceLocation Loc); + bool RequireCompleteType(SourceLocation Loc, QualType T, const PartialDiagnostic &PD, std::pair Note); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 4c8eb28f792f..471937bdb064 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -693,14 +693,17 @@ namespace clang { PREDEF_DECL_INT_128_ID = 5, /// \brief The unsigned 128-bit integer type. - PREDEF_DECL_UNSIGNED_INT_128_ID = 6 + PREDEF_DECL_UNSIGNED_INT_128_ID = 6, + + /// \brief The internal 'instancetype' typedef. + PREDEF_DECL_OBJC_INSTANCETYPE_ID = 7 }; /// \brief The number of declaration IDs that are predefined. /// /// For more information about predefined declarations, see the /// \c PredefinedDeclIDs type and the PREDEF_DECL_*_ID constants. - const unsigned int NUM_PREDEF_DECL_IDS = 7; + const unsigned int NUM_PREDEF_DECL_IDS = 8; /// \brief Record codes for each kind of declaration. /// diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index c30089445697..3a3fba5d61df 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -225,7 +225,7 @@ ASTContext::ASTContext(LangOptions& LOpts, SourceManager &SM, GlobalNestedNameSpecifier(0), Int128Decl(0), UInt128Decl(0), ObjCIdDecl(0), ObjCSelDecl(0), ObjCClassDecl(0), - CFConstantStringTypeDecl(0), + CFConstantStringTypeDecl(0), ObjCInstanceTypeDecl(0), FILEDecl(0), jmp_bufDecl(0), sigjmp_bufDecl(0), BlockDescriptorType(0), BlockDescriptorExtendedType(0), cudaConfigureCallDecl(0), @@ -3844,6 +3844,17 @@ ASTContext::BuildByRefType(StringRef DeclName, QualType Ty) const { return getPointerType(getTagDeclType(T)); } +TypedefDecl *ASTContext::getObjCInstanceTypeDecl() { + if (!ObjCInstanceTypeDecl) + ObjCInstanceTypeDecl = TypedefDecl::Create(*this, + getTranslationUnitDecl(), + SourceLocation(), + SourceLocation(), + &Idents.get("instancetype"), + getTrivialTypeSourceInfo(getObjCIdType())); + return ObjCInstanceTypeDecl; +} + // This returns true if a type has been typedefed to BOOL: // typedef BOOL; static bool isTypeTypedefedAsBOOL(QualType T) { diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index fce593fe9e93..2bf172dec1bf 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -599,6 +599,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("objc_arc", LangOpts.ObjCAutoRefCount) .Case("objc_arc_weak", LangOpts.ObjCAutoRefCount && LangOpts.ObjCRuntimeHasWeak) + .Case("objc_instancetype", LangOpts.ObjC2) .Case("objc_nonfragile_abi", LangOpts.ObjCNonFragileABI) .Case("objc_weak_class", LangOpts.ObjCNonFragileABI) .Case("ownership_holds", true) diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 5be5a6c3c2f2..1f1266cab710 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -801,8 +801,16 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS, ParseTypeName(0, Declarator::ObjCPrototypeContext, &DS); if (!TypeSpec.isInvalid()) Ty = TypeSpec.get(); + } else if (Context == OTN_ResultType && Tok.is(tok::identifier)) { + if (!Ident_instancetype) + Ident_instancetype = PP.getIdentifierInfo("instancetype"); + + if (Tok.getIdentifierInfo() == Ident_instancetype) { + Ty = Actions.ActOnObjCInstanceType(Tok.getLocation()); + ConsumeToken(); + } } - + if (Tok.is(tok::r_paren)) ConsumeParen(); else if (Tok.getLocation() == TypeStartLoc) { diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 9a26a5a3995f..c31e1634a0a6 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -443,6 +443,7 @@ void Parser::Initialize() { ObjCTypeQuals[objc_byref] = &PP.getIdentifierTable().get("byref"); } + Ident_instancetype = 0; Ident_final = 0; Ident_override = 0; diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 3e657c70e6a7..8dfdeb4b6075 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -148,8 +148,13 @@ bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, << ResultTypeRange; } - Diag(Overridden->getLocation(), diag::note_related_result_type_overridden) - << Overridden->getMethodFamily(); + if (ObjCMethodFamily Family = Overridden->getMethodFamily()) + Diag(Overridden->getLocation(), + diag::note_related_result_type_overridden_family) + << Family; + else + Diag(Overridden->getLocation(), + diag::note_related_result_type_overridden); } return false; @@ -2261,16 +2266,22 @@ bool containsInvalidMethodImplAttribute(const AttrVec &A) { return false; } +namespace { + /// \brief Describes the compatibility of a result type with its method. + enum ResultTypeCompatibilityKind { + RTC_Compatible, + RTC_Incompatible, + RTC_Unknown + }; +} + /// \brief Check whether the declared result type of the given Objective-C /// method declaration is compatible with the method's class. /// -static bool +static ResultTypeCompatibilityKind CheckRelatedResultTypeCompatibility(Sema &S, ObjCMethodDecl *Method, ObjCInterfaceDecl *CurrentClass) { QualType ResultType = Method->getResultType(); - SourceRange ResultTypeRange; - if (const TypeSourceInfo *ResultTypeInfo = Method->getResultTypeSourceInfo()) - ResultTypeRange = ResultTypeInfo->getTypeLoc().getSourceRange(); // If an Objective-C method inherits its related result type, then its // declared result type must be compatible with its own class type. The @@ -2280,23 +2291,27 @@ CheckRelatedResultTypeCompatibility(Sema &S, ObjCMethodDecl *Method, // - it is id or qualified id, or if (ResultObjectType->isObjCIdType() || ResultObjectType->isObjCQualifiedIdType()) - return false; + return RTC_Compatible; if (CurrentClass) { if (ObjCInterfaceDecl *ResultClass = ResultObjectType->getInterfaceDecl()) { // - it is the same as the method's class type, or if (CurrentClass == ResultClass) - return false; + return RTC_Compatible; // - it is a superclass of the method's class type if (ResultClass->isSuperClassOf(CurrentClass)) - return false; + return RTC_Compatible; } + } else { + // Any Objective-C pointer type might be acceptable for a protocol + // method; we just don't know. + return RTC_Unknown; } } - return true; + return RTC_Incompatible; } namespace { @@ -2457,6 +2472,7 @@ Decl *Sema::ActOnMethodDeclaration( Decl *ClassDecl = cast(OCD); QualType resultDeclType; + bool HasRelatedResultType = false; TypeSourceInfo *ResultTInfo = 0; if (ReturnType) { resultDeclType = GetTypeFromParser(ReturnType, &ResultTInfo); @@ -2468,6 +2484,8 @@ Decl *Sema::ActOnMethodDeclaration( << 0 << resultDeclType; return 0; } + + HasRelatedResultType = (resultDeclType == Context.getObjCInstanceType()); } else { // get the type for "id". resultDeclType = Context.getObjCIdType(); Diag(MethodLoc, diag::warn_missing_method_return_type) @@ -2484,7 +2502,7 @@ Decl *Sema::ActOnMethodDeclaration( MethodDeclKind == tok::objc_optional ? ObjCMethodDecl::Optional : ObjCMethodDecl::Required, - false); + HasRelatedResultType); SmallVector Params; @@ -2604,9 +2622,8 @@ Decl *Sema::ActOnMethodDeclaration( CurrentClass = CatImpl->getClassInterface(); } - bool isRelatedResultTypeCompatible = - (getLangOptions().ObjCInferRelatedResultType && - !CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass)); + ResultTypeCompatibilityKind RTC + = CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass); // Search for overridden methods and merge information down from them. OverrideSearch overrides(*this, ObjCMethod); @@ -2615,7 +2632,7 @@ Decl *Sema::ActOnMethodDeclaration( ObjCMethodDecl *overridden = *i; // Propagate down the 'related result type' bit from overridden methods. - if (isRelatedResultTypeCompatible && overridden->hasRelatedResultType()) + if (RTC != RTC_Incompatible && overridden->hasRelatedResultType()) ObjCMethod->SetRelatedResultType(); // Then merge the declarations. @@ -2633,8 +2650,10 @@ Decl *Sema::ActOnMethodDeclaration( if (getLangOptions().ObjCAutoRefCount) ARCError = CheckARCMethodDecl(*this, ObjCMethod); - if (!ARCError && isRelatedResultTypeCompatible && - !ObjCMethod->hasRelatedResultType()) { + // Infer the related result type when possible. + if (!ARCError && RTC == RTC_Compatible && + !ObjCMethod->hasRelatedResultType() && + LangOpts.ObjCInferRelatedResultType) { bool InferRelatedResultType = false; switch (ObjCMethod->getMethodFamily()) { case OMF_None: diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index aa9b4748a039..9f2696e12888 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -328,6 +328,10 @@ void Sema::EmitRelatedResultTypeNote(const Expr *E) { MsgSend->getType())) return; + if (!Context.hasSameUnqualifiedType(Method->getResultType(), + Context.getObjCInstanceType())) + return; + Diag(Method->getLocation(), diag::note_related_result_type_inferred) << Method->isInstanceMethod() << Method->getSelector() << MsgSend->getType(); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index c89423037b4a..e48704ca1df8 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3053,6 +3053,13 @@ TypeResult Sema::ActOnTypeName(Scope *S, Declarator &D) { return CreateParsedType(T, TInfo); } +ParsedType Sema::ActOnObjCInstanceType(SourceLocation Loc) { + QualType T = Context.getObjCInstanceType(); + TypeSourceInfo *TInfo = Context.getTrivialTypeSourceInfo(T, Loc); + return CreateParsedType(T, TInfo); +} + + //===----------------------------------------------------------------------===// // Type Attribute Processing //===----------------------------------------------------------------------===// diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 37a23faf0dce..251d0f6e3170 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -4126,6 +4126,10 @@ Decl *ASTReader::GetDecl(DeclID ID) { case PREDEF_DECL_UNSIGNED_INT_128_ID: assert(Context && "No context available?"); return Context->getUInt128Decl(); + + case PREDEF_DECL_OBJC_INSTANCETYPE_ID: + assert(Context && "No context available?"); + return Context->getObjCInstanceTypeDecl(); } return 0; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 733f5d0583e1..bdca689d6f01 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -2880,6 +2880,8 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, DeclIDs[Context.Int128Decl] = PREDEF_DECL_INT_128_ID; if (Context.UInt128Decl) DeclIDs[Context.UInt128Decl] = PREDEF_DECL_UNSIGNED_INT_128_ID; + if (Context.ObjCInstanceTypeDecl) + DeclIDs[Context.ObjCInstanceTypeDecl] = PREDEF_DECL_OBJC_INSTANCETYPE_ID; if (!Chain) { // Make sure that we emit IdentifierInfos (and any attached diff --git a/clang/test/PCH/objc_methods.h b/clang/test/PCH/objc_methods.h index bd775354349f..c9b1ad4342c6 100644 --- a/clang/test/PCH/objc_methods.h +++ b/clang/test/PCH/objc_methods.h @@ -2,7 +2,7 @@ @interface TestPCH + alloc; -- (id)init; +- (instancetype)instMethod; @end @class TestForwardClassDecl; diff --git a/clang/test/PCH/objc_methods.m b/clang/test/PCH/objc_methods.m index 3311813c98df..e90a463dce6b 100644 --- a/clang/test/PCH/objc_methods.m +++ b/clang/test/PCH/objc_methods.m @@ -12,5 +12,5 @@ void func() { // AliasForTestPCH *zz; xx = [TestPCH alloc]; - [xx init]; + [xx instMethod]; } diff --git a/clang/test/SemaObjC/instancetype.m b/clang/test/SemaObjC/instancetype.m new file mode 100644 index 000000000000..13d6e0309f82 --- /dev/null +++ b/clang/test/SemaObjC/instancetype.m @@ -0,0 +1,190 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#if !__has_feature(objc_instancetype) +# error Missing 'instancetype' feature macro. +#endif + +@interface Root ++ (instancetype)alloc; +- (instancetype)init; // expected-note{{overridden method is part of the 'init' method family}} +- (instancetype)self; +- (Class)class; + +@property (assign) Root *selfProp; +- (instancetype)selfProp; +@end + +@protocol Proto1 +@optional +- (instancetype)methodInProto1; +@end + +@protocol Proto2 +@optional +- (instancetype)methodInProto2; // expected-note{{overridden method returns an instance of its class type}} +- (instancetype)otherMethodInProto2; // expected-note{{overridden method returns an instance of its class type}} +@end + +@interface Subclass1 : Root +- (instancetype)initSubclass1; +- (void)methodOnSubclass1; ++ (instancetype)allocSubclass1; +@end + +@interface Subclass2 : Root +- (instancetype)initSubclass2; +- (void)methodOnSubclass2; +@end + +// Sanity check: the basic initialization pattern. +void test_instancetype_alloc_init_simple() { + Root *r1 = [[Root alloc] init]; + Subclass1 *sc1 = [[Subclass1 alloc] init]; +} + +// Test that message sends to instancetype methods have the right type. +void test_instancetype_narrow_method_search() { + // instancetype on class methods + Subclass1 *sc1 = [[Subclass1 alloc] initSubclass2]; // expected-warning{{'Subclass1' may not respond to 'initSubclass2'}} + Subclass2 *sc2 = [[Subclass2 alloc] initSubclass2]; // okay + + // instancetype on instance methods + [[[Subclass1 alloc] init] methodOnSubclass2]; // expected-warning{{'Subclass1' may not respond to 'methodOnSubclass2'}} + [[[Subclass2 alloc] init] methodOnSubclass2]; + + // instancetype on class methods using protocols + typedef Subclass1 SC1Proto1; + typedef Subclass1 SC1Proto2; + [[SC1Proto1 alloc] methodInProto2]; // expected-warning{{method '-methodInProto2' not found (return type defaults to 'id')}} + [[SC1Proto2 alloc] methodInProto2]; + + // instancetype on instance methods + Subclass1 *sc1proto1 = 0; + [[sc1proto1 self] methodInProto2]; // expected-warning{{method '-methodInProto2' not found (return type defaults to 'id')}} + Subclass1 *sc1proto2 = 0; + [[sc1proto2 self] methodInProto2]; + + // Exact type checks + typeof([[Subclass1 alloc] init]) *ptr1 = (Subclass1 **)0; + typeof([[Subclass2 alloc] init]) *ptr2 = (Subclass2 **)0; + + // Message sends to Class. + Subclass1 *sc1proto1_2 = [[[sc1proto1 class] alloc] init]; + + // Property access + [sc1proto1.self methodInProto2]; // expected-warning{{method '-methodInProto2' not found (return type defaults to 'id')}} + [sc1proto2.self methodInProto2]; + [Subclass1.alloc initSubclass2]; // expected-warning{{'Subclass1' may not respond to 'initSubclass2'}} + [Subclass2.alloc initSubclass2]; + + [sc1proto1.selfProp methodInProto2]; // expected-warning{{method '-methodInProto2' not found (return type defaults to 'id')}} + [sc1proto2.selfProp methodInProto2]; +} + +// Test that message sends to super methods have the right type. +@interface Subsubclass1 : Subclass1 +- (instancetype)initSubclass1; ++ (instancetype)allocSubclass1; + +- (void)onlyInSubsubclass1; +@end + +@implementation Subsubclass1 +- (instancetype)initSubclass1 { + // Check based on method search. + [[super initSubclass1] methodOnSubclass2]; // expected-warning{{'Subsubclass1' may not respond to 'methodOnSubclass2'}} + [super.initSubclass1 methodOnSubclass2]; // expected-warning{{'Subsubclass1' may not respond to 'methodOnSubclass2'}} + + self = [super init]; // common pattern + + // Exact type check. + typeof([super initSubclass1]) *ptr1 = (Subsubclass1**)0; + + return self; +} + ++ (instancetype)allocSubclass1 { + // Check based on method search. + [[super allocSubclass1] methodOnSubclass2]; // expected-warning{{'Subsubclass1' may not respond to 'methodOnSubclass2'}} + + // The ASTs don't model super property accesses well enough to get this right + [super.allocSubclass1 methodOnSubclass2]; // expected-warning{{'Subsubclass1' may not respond to 'methodOnSubclass2'}} + + // Exact type check. + typeof([super allocSubclass1]) *ptr1 = (Subsubclass1**)0; + + return [super allocSubclass1]; +} + +- (void)onlyInSubsubclass1 {} +@end + +// Check compatibility rules for inheritance of related return types. +@class Subclass4; + +@interface Subclass3 +- (Subclass3 *)methodInProto1; +- (Subclass4 *)methodInProto2; // expected-warning{{method is expected to return an instance of its class type 'Subclass3', but is declared to return 'Subclass4 *'}} +@end + +@interface Subclass4 : Root ++ (Subclass4 *)alloc; // okay +- (Subclass3 *)init; // expected-warning{{method is expected to return an instance of its class type 'Subclass4', but is declared to return 'Subclass3 *'}} +- (id)self; // expected-note{{overridden method is part of the 'self' method family}} +- (instancetype)initOther; +@end + +@protocol Proto3 +@optional +- (id)methodInProto1; +- (Subclass1 *)methodInProto2; +- (int)otherMethodInProto2; // expected-warning{{protocol method is expected to return an instance of the implementing class, but is declared to return 'int'}} +@end + +@implementation Subclass4 ++ (id)alloc { + return self; // expected-warning{{incompatible pointer types returning 'Class' from a function with result type 'Subclass4 *'}} +} + +- (Subclass3 *)init { return 0; } // don't complain: we lost the related return type + +- (Subclass3 *)self { return 0; } // expected-warning{{method is expected to return an instance of its class type 'Subclass4', but is declared to return 'Subclass3 *'}} + +- (Subclass4 *)initOther { return 0; } + +@end + +// Check that inherited related return types influence the types of +// message sends. +void test_instancetype_inherited() { + [[Subclass4 alloc] initSubclass1]; // expected-warning{{'Subclass4' may not respond to 'initSubclass1'}} + [[Subclass4 alloc] initOther]; +} + +// Check that related return types tighten up the semantics of +// Objective-C method implementations. +@implementation Subclass2 +- (instancetype)initSubclass2 { + Subclass1 *sc1 = [[Subclass1 alloc] init]; + return sc1; // expected-warning{{incompatible pointer types returning 'Subclass1 *' from a function with result type 'Subclass2 *'}} +} +- (void)methodOnSubclass2 {} +- (id)self { + Subclass1 *sc1 = [[Subclass1 alloc] init]; + return sc1; // expected-warning{{incompatible pointer types returning 'Subclass1 *' from a function with result type 'Subclass2 *'}} +} +@end + +@interface MyClass : Root ++ (int)myClassMethod; +@end + +@implementation MyClass ++ (int)myClassMethod { return 0; } + +- (void)blah { + int i = [[MyClass self] myClassMethod]; +} + +@end +