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
+