aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFariborz Jahanian <fjahanian@apple.com>2007-12-17 21:03:50 +0000
committerFariborz Jahanian <fjahanian@apple.com>2007-12-17 21:03:50 +0000
commitc569249ca0ab755ac79d8cbbfcb2bcae19743624 (patch)
tree3f2598d15d6ca1e52e8418bd4d1191f49a751019
parent2f6974a14c28a9ea56232fdd926c430f755bdbbf (diff)
Patch to implemented objective-c's dynamic object pointer qualified with
the protocol list (id<P,...> types). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@45121 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--AST/ASTContext.cpp45
-rw-r--r--AST/Type.cpp27
-rw-r--r--AST/TypeSerialization.cpp2
-rw-r--r--CodeGen/CodeGenTypes.cpp4
-rw-r--r--Driver/RewriteTest.cpp29
-rw-r--r--Sema/SemaExpr.cpp30
-rw-r--r--Sema/SemaType.cpp10
-rw-r--r--clang.xcodeproj/project.pbxproj1
-rw-r--r--include/clang/AST/ASTContext.h8
-rw-r--r--include/clang/AST/Type.h39
-rw-r--r--include/clang/Basic/DiagnosticKinds.def2
-rw-r--r--test/Sema/id-test-3.m14
-rw-r--r--test/Sema/protocol-id-test-1.m16
-rw-r--r--test/Sema/protocol-id-test-2.m14
14 files changed, 226 insertions, 15 deletions
diff --git a/AST/ASTContext.cpp b/AST/ASTContext.cpp
index 4351b89e96..92b691ee90 100644
--- a/AST/ASTContext.cpp
+++ b/AST/ASTContext.cpp
@@ -49,6 +49,7 @@ void ASTContext::PrintStats() const {
unsigned NumTagStruct = 0, NumTagUnion = 0, NumTagEnum = 0, NumTagClass = 0;
unsigned NumObjcInterfaces = 0, NumObjcQualifiedInterfaces = 0;
+ unsigned NumObjcQualifiedIds = 0;
for (unsigned i = 0, e = Types.size(); i != e; ++i) {
Type *T = Types[i];
@@ -83,6 +84,8 @@ void ASTContext::PrintStats() const {
++NumObjcInterfaces;
else if (isa<ObjcQualifiedInterfaceType>(T))
++NumObjcQualifiedInterfaces;
+ else if (isa<ObjcQualifiedIdType>(T))
+ ++NumObjcQualifiedIds;
else {
QualType(T, 0).dump();
assert(0 && "Unknown type!");
@@ -106,6 +109,8 @@ void ASTContext::PrintStats() const {
fprintf(stderr, " %d interface types\n", NumObjcInterfaces);
fprintf(stderr, " %d protocol qualified interface types\n",
NumObjcQualifiedInterfaces);
+ fprintf(stderr, " %d protocol qualified id types\n",
+ NumObjcQualifiedIds);
fprintf(stderr, "Total bytes = %d\n", int(NumBuiltin*sizeof(BuiltinType)+
NumPointer*sizeof(PointerType)+NumArray*sizeof(ArrayType)+
NumComplex*sizeof(ComplexType)+NumVector*sizeof(VectorType)+
@@ -677,7 +682,7 @@ QualType ASTContext::getTypedefType(TypedefDecl *Decl) {
if (Decl->TypeForDecl) return QualType(Decl->TypeForDecl, 0);
QualType Canonical = Decl->getUnderlyingType().getCanonicalType();
- Decl->TypeForDecl = new TypedefType(Decl, Canonical);
+ Decl->TypeForDecl = new TypedefType(Type::TypeName, Decl, Canonical);
Types.push_back(Decl->TypeForDecl);
return QualType(Decl->TypeForDecl, 0);
}
@@ -713,6 +718,29 @@ QualType ASTContext::getObjcQualifiedInterfaceType(ObjcInterfaceDecl *Decl,
return QualType(QType, 0);
}
+/// getObjcQualifiedIdType - Return a
+/// getObjcQualifiedIdType type for the given interface decl and
+/// the conforming protocol list.
+QualType ASTContext::getObjcQualifiedIdType(TypedefDecl *Decl,
+ ObjcProtocolDecl **Protocols,
+ unsigned NumProtocols) {
+ llvm::FoldingSetNodeID ID;
+ ObjcQualifiedIdType::Profile(ID, Protocols, NumProtocols);
+
+ void *InsertPos = 0;
+ if (ObjcQualifiedIdType *QT =
+ ObjcQualifiedIdTypes.FindNodeOrInsertPos(ID, InsertPos))
+ return QualType(QT, 0);
+
+ // No Match;
+ QualType Canonical = Decl->getUnderlyingType().getCanonicalType();
+ ObjcQualifiedIdType *QType =
+ new ObjcQualifiedIdType(Decl, Canonical, Protocols, NumProtocols);
+ Types.push_back(QType);
+ ObjcQualifiedIdTypes.InsertNode(QType, InsertPos);
+ return QualType(QType, 0);
+}
+
/// getTypeOfExpr - Unlike many "get<Type>" functions, we can't unique
/// TypeOfExpr AST's (since expression's are never shared). For example,
/// multiple declarations that refer to "typeof(x)" all contain different
@@ -1043,7 +1071,13 @@ void ASTContext::getObjcEncodingForType(QualType T, std::string& S) const
}
S += encoding;
- } else if (const PointerType *PT = T->getAsPointerType()) {
+ }
+ else if (const ObjcQualifiedIdType *QIT = dyn_cast<ObjcQualifiedIdType>(T)) {
+ // Treat id<P...> same as 'id' for encoding purposes.
+ return getObjcEncodingForType(QIT->getDecl()->getUnderlyingType(), S);
+
+ }
+ else if (const PointerType *PT = T->getAsPointerType()) {
QualType PointeeTy = PT->getPointeeType();
if (isObjcIdType(PointeeTy) || PointeeTy->isObjcInterfaceType()) {
S += '@';
@@ -1221,6 +1255,13 @@ bool ASTContext::QualifiedInterfaceTypesAreCompatible(QualType lhs,
return true;
}
+// TODO: id<P1,...> vs. id<P,...>
+#if 0
+bool ASTContext::QualifiedIdTypesAreCompatible(QualType lhs,
+ QualType rhs) {
+}
+#endif
+
bool ASTContext::vectorTypesAreCompatible(QualType lhs, QualType rhs) {
const VectorType *lVector = lhs->getAsVectorType();
const VectorType *rVector = rhs->getAsVectorType();
diff --git a/AST/Type.cpp b/AST/Type.cpp
index b1da04b0a3..63f3589769 100644
--- a/AST/Type.cpp
+++ b/AST/Type.cpp
@@ -539,6 +539,17 @@ void ObjcQualifiedInterfaceType::Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, &Protocols[0], getNumProtocols());
}
+void ObjcQualifiedIdType::Profile(llvm::FoldingSetNodeID &ID,
+ ObjcProtocolDecl **protocols,
+ unsigned NumProtocols) {
+ for (unsigned i = 0; i != NumProtocols; i++)
+ ID.AddPointer(protocols[i]);
+}
+
+void ObjcQualifiedIdType::Profile(llvm::FoldingSetNodeID &ID) {
+ Profile(ID, &Protocols[0], getNumProtocols());
+}
+
/// LookThroughTypedefs - Return the ultimate type this typedef corresponds to
/// potentially looking through *all* consequtive typedefs. This returns the
/// sum of the type qualifiers, so if you have:
@@ -780,6 +791,22 @@ void ObjcQualifiedInterfaceType::getAsStringInternal(
InnerString = ObjcQIString + InnerString;
}
+void ObjcQualifiedIdType::getAsStringInternal(
+ std::string &InnerString) const {
+ if (!InnerString.empty()) // Prefix the basic type, e.g. 'typedefname X'.
+ InnerString = ' ' + InnerString;
+ std::string ObjcQIString = "id";
+ ObjcQIString += '<';
+ int num = getNumProtocols();
+ for (int i = 0; i < num; i++) {
+ ObjcQIString += getProtocols(i)->getName();
+ if (i < num-1)
+ ObjcQIString += ',';
+ }
+ ObjcQIString += '>';
+ InnerString = ObjcQIString + InnerString;
+}
+
void TagType::getAsStringInternal(std::string &InnerString) const {
if (!InnerString.empty()) // Prefix the basic type, e.g. 'typedefname X'.
InnerString = ' ' + InnerString;
diff --git a/AST/TypeSerialization.cpp b/AST/TypeSerialization.cpp
index 10965c2421..a3b99ed8a5 100644
--- a/AST/TypeSerialization.cpp
+++ b/AST/TypeSerialization.cpp
@@ -224,7 +224,7 @@ Type* TypedefType::CreateImpl(ASTContext& Context, Deserializer& D) {
std::vector<Type*>& Types =
const_cast<std::vector<Type*>&>(Context.getTypes());
- TypedefType* T = new TypedefType(NULL,QualType::ReadVal(D));
+ TypedefType* T = new TypedefType(Type::TypeName, NULL,QualType::ReadVal(D));
Types.push_back(T);
D.ReadPtr(T->Decl); // May be backpatched.
diff --git a/CodeGen/CodeGenTypes.cpp b/CodeGen/CodeGenTypes.cpp
index cc40af8f94..ac223bcd24 100644
--- a/CodeGen/CodeGenTypes.cpp
+++ b/CodeGen/CodeGenTypes.cpp
@@ -251,6 +251,10 @@ const llvm::Type *CodeGenTypes::ConvertNewType(QualType T) {
assert(0 && "FIXME: add missing functionality here");
break;
+ case Type::ObjcQualifiedId:
+ assert(0 && "FIXME: add missing functionality here");
+ break;
+
case Type::Tagged:
const TagType &TT = cast<TagType>(Ty);
const TagDecl *TD = TT.getDecl();
diff --git a/Driver/RewriteTest.cpp b/Driver/RewriteTest.cpp
index 37f2901ad2..30b1e98af0 100644
--- a/Driver/RewriteTest.cpp
+++ b/Driver/RewriteTest.cpp
@@ -490,7 +490,10 @@ void RewriteTest::RewriteForwardProtocolDecl(ObjcForwardProtocolDecl *PDecl) {
void RewriteTest::RewriteObjcMethodDecl(ObjcMethodDecl *OMD,
std::string &ResultStr) {
ResultStr += "\nstatic ";
- ResultStr += OMD->getResultType().getAsString();
+ if (isa<ObjcQualifiedIdType>(OMD->getResultType()))
+ ResultStr += "id";
+ else
+ ResultStr += OMD->getResultType().getAsString();
ResultStr += "\n";
// Unique method name
@@ -548,7 +551,10 @@ void RewriteTest::RewriteObjcMethodDecl(ObjcMethodDecl *OMD,
for (int i = 0; i < OMD->getNumParams(); i++) {
ParmVarDecl *PDecl = OMD->getParamDecl(i);
ResultStr += ", ";
- ResultStr += PDecl->getType().getAsString();
+ if (isa<ObjcQualifiedIdType>(PDecl->getType()))
+ ResultStr += "id";
+ else
+ ResultStr += PDecl->getType().getAsString();
ResultStr += " ";
ResultStr += PDecl->getName();
}
@@ -989,10 +995,13 @@ static void scanToNextArgument(const char *&argRef) {
}
bool RewriteTest::needToScanForQualifiers(QualType T) {
- // FIXME: we don't currently represent "id <Protocol>" in the type system.
+
if (T == Context->getObjcIdType())
return true;
+ if (isa<ObjcQualifiedIdType>(T))
+ return true;
+
if (const PointerType *pType = T->getAsPointerType()) {
Type *pointeeType = pType->getPointeeType().getTypePtr();
if (isa<ObjcQualifiedInterfaceType>(pointeeType))
@@ -1311,6 +1320,9 @@ ObjcInterfaceDecl *RewriteTest::isSuperReceiver(Expr *recExpr) {
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CE->getSubExpr())) {
if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) {
if (!strcmp(PVD->getName(), "self")) {
+ // is this id<P1..> type?
+ if (isa<ObjcQualifiedIdType>(CE->getType()))
+ return 0;
if (const PointerType *PT = CE->getType()->getAsPointerType()) {
if (ObjcInterfaceType *IT =
dyn_cast<ObjcInterfaceType>(PT->getPointeeType())) {
@@ -1504,7 +1516,9 @@ Stmt *RewriteTest::RewriteMessageExpr(ObjCMessageExpr *Exp) {
// Make all implicit casts explicit...ICE comes in handy:-)
if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(userExpr)) {
// Reuse the ICE type, it is exactly what the doctor ordered.
- userExpr = new CastExpr(ICE->getType(), userExpr, SourceLocation());
+ userExpr = new CastExpr(isa<ObjcQualifiedIdType>(ICE->getType())
+ ? Context->getObjcIdType()
+ : ICE->getType(), userExpr, SourceLocation());
}
MsgExprs.push_back(userExpr);
// We've transferred the ownership to MsgExprs. Null out the argument in
@@ -1525,10 +1539,13 @@ Stmt *RewriteTest::RewriteMessageExpr(ObjCMessageExpr *Exp) {
if (ObjcMethodDecl *mDecl = Exp->getMethodDecl()) {
// Push any user argument types.
for (int i = 0; i < mDecl->getNumParams(); i++) {
- QualType t = mDecl->getParamDecl(i)->getType();
+ QualType t = isa<ObjcQualifiedIdType>(mDecl->getParamDecl(i)->getType())
+ ? Context->getObjcIdType()
+ : mDecl->getParamDecl(i)->getType();
ArgTypes.push_back(t);
}
- returnType = mDecl->getResultType();
+ returnType = isa<ObjcQualifiedIdType>(mDecl->getResultType())
+ ? Context->getObjcIdType() : mDecl->getResultType();
} else {
returnType = Context->getObjcIdType();
}
diff --git a/Sema/SemaExpr.cpp b/Sema/SemaExpr.cpp
index dae5325ac4..45d0470544 100644
--- a/Sema/SemaExpr.cpp
+++ b/Sema/SemaExpr.cpp
@@ -2292,13 +2292,16 @@ Sema::ExprResult Sema::ActOnInstanceMessage(
return true;
}
} else {
+ bool receiverIsQualId =
+ dyn_cast<ObjcQualifiedIdType>(RExpr->getType()) != 0;
// FIXME (snaroff): checking in this code from Patrick. Needs to be
// revisited. how do we get the ClassDecl from the receiver expression?
- while (receiverType->isPointerType()) {
- PointerType *pointerType =
- static_cast<PointerType*>(receiverType.getTypePtr());
- receiverType = pointerType->getPointeeType();
- }
+ if (!receiverIsQualId)
+ while (receiverType->isPointerType()) {
+ PointerType *pointerType =
+ static_cast<PointerType*>(receiverType.getTypePtr());
+ receiverType = pointerType->getPointeeType();
+ }
ObjcInterfaceDecl* ClassDecl;
if (ObjcQualifiedInterfaceType *QIT =
dyn_cast<ObjcQualifiedInterfaceType>(receiverType)) {
@@ -2312,6 +2315,23 @@ Sema::ExprResult Sema::ActOnInstanceMessage(
break;
}
}
+ if (!Method)
+ Diag(lbrac, diag::warn_method_not_found_in_protocol,
+ std::string("-"), Sel.getName(),
+ SourceRange(lbrac, rbrac));
+ }
+ else if (ObjcQualifiedIdType *QIT =
+ dyn_cast<ObjcQualifiedIdType>(receiverType)) {
+ // search protocols
+ for (unsigned i = 0; i < QIT->getNumProtocols(); i++) {
+ ObjcProtocolDecl *PDecl = QIT->getProtocols(i);
+ if (PDecl && (Method = PDecl->lookupInstanceMethod(Sel)))
+ break;
+ }
+ if (!Method)
+ Diag(lbrac, diag::warn_method_not_found_in_protocol,
+ std::string("-"), Sel.getName(),
+ SourceRange(lbrac, rbrac));
}
else {
assert(ObjcInterfaceType::classof(receiverType.getTypePtr()) &&
diff --git a/Sema/SemaType.cpp b/Sema/SemaType.cpp
index 4eefabfe6c..c89018a677 100644
--- a/Sema/SemaType.cpp
+++ b/Sema/SemaType.cpp
@@ -113,6 +113,16 @@ static QualType ConvertDeclSpecToType(const DeclSpec &DS, ASTContext &Ctx) {
reinterpret_cast<ObjcProtocolDecl**>(PPDecl),
DS.NumProtocolQualifiers());
}
+ else if (TypedefDecl *typeDecl = dyn_cast<TypedefDecl>(D)) {
+ if (Ctx.getObjcIdType() == Ctx.getTypedefType(typeDecl)
+ && DS.getProtocolQualifiers()) {
+ // id<protocol-list>
+ Action::DeclTy **PPDecl = &(*DS.getProtocolQualifiers())[0];
+ return Ctx.getObjcQualifiedIdType(typeDecl,
+ reinterpret_cast<ObjcProtocolDecl**>(PPDecl),
+ DS.NumProtocolQualifiers());
+ }
+ }
// TypeQuals handled by caller.
return Ctx.getTypedefType(cast<TypedefDecl>(D));
}
diff --git a/clang.xcodeproj/project.pbxproj b/clang.xcodeproj/project.pbxproj
index 892e53b2a1..26353e5b1f 100644
--- a/clang.xcodeproj/project.pbxproj
+++ b/clang.xcodeproj/project.pbxproj
@@ -777,6 +777,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "clang" */;
+ compatibilityVersion = "Xcode 2.4";
hasScannedForEncodings = 1;
mainGroup = 08FB7794FE84155DC02AAC07 /* clang */;
projectDirPath = "";
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index f28ae3f4c2..058127544e 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -42,6 +42,7 @@ class ASTContext {
llvm::FoldingSet<FunctionTypeNoProto> FunctionTypeNoProtos;
llvm::FoldingSet<FunctionTypeProto> FunctionTypeProtos;
llvm::FoldingSet<ObjcQualifiedInterfaceType> ObjcQualifiedInterfaceTypes;
+ llvm::FoldingSet<ObjcQualifiedIdType> ObjcQualifiedIdTypes;
/// ASTRecordLayouts - A cache mapping from RecordDecls to ASTRecordLayouts.
/// This is lazily created. This is intentionally not serialized.
llvm::DenseMap<const RecordDecl*, const ASTRecordLayout*> ASTRecordLayouts;
@@ -168,6 +169,13 @@ public:
/// the conforming protocol list.
QualType getObjcQualifiedInterfaceType(ObjcInterfaceDecl *Decl,
ObjcProtocolDecl **ProtocolList, unsigned NumProtocols);
+
+ /// getObjcQualifiedIdType - Return an ObjcQualifiedIdType for a
+ /// given 'id' and conforming protocol list.
+ QualType getObjcQualifiedIdType(TypedefDecl *Decl,
+ ObjcProtocolDecl **ProtocolList,
+ unsigned NumProtocols);
+
/// getTypeOfType - GCC extension.
QualType getTypeOfExpr(Expr *e);
diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h
index da44891780..0661f57e69 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -220,6 +220,7 @@ public:
FunctionNoProto, FunctionProto,
TypeName, Tagged,
ObjcInterface, ObjcQualifiedInterface,
+ ObjcQualifiedId,
TypeOfExp, TypeOfTyp // GNU typeof extension.
};
private:
@@ -819,7 +820,9 @@ protected:
class TypedefType : public Type {
TypedefDecl *Decl;
- TypedefType(TypedefDecl *D, QualType can) : Type(TypeName, can), Decl(D) {
+protected:
+ TypedefType(TypeClass tc, TypedefDecl *D, QualType can)
+ : Type(tc, can), Decl(D) {
assert(!isa<TypedefType>(can) && "Invalid canonical type");
}
friend class ASTContext; // ASTContext creates these.
@@ -951,6 +954,40 @@ public:
}
static bool classof(const ObjcQualifiedInterfaceType *) { return true; }
};
+
+/// ObjcQualifiedIdType - to represent id<protocol-list>
+class ObjcQualifiedIdType : public TypedefType,
+ public llvm::FoldingSetNode {
+ // List of protocols for this protocol conforming 'id' type
+ // List is sorted on protocol name. No protocol is enterred more than once.
+ llvm::SmallVector<ObjcProtocolDecl*, 8> Protocols;
+
+ ObjcQualifiedIdType(TypedefDecl *TD, QualType can,
+ ObjcProtocolDecl **Protos, unsigned NumP) :
+ TypedefType(ObjcQualifiedId, TD, can),
+ Protocols(Protos, Protos+NumP) { }
+ friend class ASTContext; // ASTContext creates these.
+public:
+
+ ObjcProtocolDecl *getProtocols(unsigned i) const {
+ return Protocols[i];
+ }
+ unsigned getNumProtocols() const {
+ return Protocols.size();
+ }
+
+ virtual void getAsStringInternal(std::string &InnerString) const;
+
+ void Profile(llvm::FoldingSetNodeID &ID);
+ static void Profile(llvm::FoldingSetNodeID &ID,
+ ObjcProtocolDecl **protocols, unsigned NumProtocols);
+
+ static bool classof(const Type *T) {
+ return T->getTypeClass() == ObjcQualifiedId;
+ }
+ static bool classof(const ObjcQualifiedIdType *) { return true; }
+
+};
/// RecordType - This is a helper class that allows the use of isa/cast/dyncast
/// to detect TagType objects of structs/unions/classes.
diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def
index 684141a367..811576e9e7 100644
--- a/include/clang/Basic/DiagnosticKinds.def
+++ b/include/clang/Basic/DiagnosticKinds.def
@@ -470,6 +470,8 @@ DIAG(err_statically_allocated_object, ERROR,
"statically allocated Objective-C object '%0'")
DIAG(warn_method_not_found, WARNING,
"method '%0%1' not found (return type defaults to 'id')")
+DIAG(warn_method_not_found_in_protocol, WARNING,
+ "method '%0%1' not found in protocol (return type defaults to 'id')")
//===----------------------------------------------------------------------===//
// Semantic Analysis
diff --git a/test/Sema/id-test-3.m b/test/Sema/id-test-3.m
new file mode 100644
index 0000000000..5331888779
--- /dev/null
+++ b/test/Sema/id-test-3.m
@@ -0,0 +1,14 @@
+// RUN: clang -rewrite-test %s
+
+@protocol P
+- (id<P>) Meth: (id<P>) Arg;
+@end
+
+@interface INTF<P>
+- (id<P>)IMeth;
+@end
+
+@implementation INTF
+- (id<P>)IMeth { return [(id<P>)self Meth: 0]; }
+- (id<P>) Meth : (id<P>) Arg {}
+@end
diff --git a/test/Sema/protocol-id-test-1.m b/test/Sema/protocol-id-test-1.m
new file mode 100644
index 0000000000..765500e10d
--- /dev/null
+++ b/test/Sema/protocol-id-test-1.m
@@ -0,0 +1,16 @@
+// RUN: clang -verify %s
+
+@interface FF
+- (void) Meth;
+@end
+
+@protocol P
+@end
+
+@interface INTF<P>
+- (void)IMeth;
+@end
+
+@implementation INTF
+- (void)IMeth {INTF<P> *pi; [pi Meth]; } // expected-warning {{method '-Meth' not found in protocol (return type defaults to 'id')}}
+@end
diff --git a/test/Sema/protocol-id-test-2.m b/test/Sema/protocol-id-test-2.m
new file mode 100644
index 0000000000..525d2cca9e
--- /dev/null
+++ b/test/Sema/protocol-id-test-2.m
@@ -0,0 +1,14 @@
+// RUN: clang -verify %s
+
+@protocol P
+@end
+
+@interface INTF<P>
+- (void)IMeth;
+ - (void) Meth;
+@end
+
+@implementation INTF
+- (void)IMeth { [(id<P>)self Meth]; } // expected-warning {{method '-Meth' not found in protocol (return type defaults to 'id')}}
+- (void) Meth {}
+@end