SemaExprObjC.cpp revision 4b9846d221146a015478b9bd4745f3a84aed1c56
1//===--- SemaExprObjC.cpp - Semantic Analysis for ObjC Expressions --------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//  This file implements semantic analysis for Objective-C expressions.
11//
12//===----------------------------------------------------------------------===//
13
14#include "Sema.h"
15#include "clang/AST/ASTContext.h"
16#include "clang/AST/DeclObjC.h"
17#include "clang/AST/ExprObjC.h"
18#include "clang/Basic/Diagnostic.h"
19using namespace clang;
20
21Sema::ExprResult Sema::ParseObjCStringLiteral(SourceLocation *AtLocs,
22                                              ExprTy **Strings,
23                                              unsigned NumStrings) {
24  SourceLocation AtLoc = AtLocs[0];
25  StringLiteral* S = static_cast<StringLiteral *>(Strings[0]);
26  if (NumStrings > 1) {
27    // Concatenate objc strings.
28    StringLiteral* ES = static_cast<StringLiteral *>(Strings[NumStrings-1]);
29    SourceLocation EndLoc = ES->getSourceRange().getEnd();
30    unsigned Length = 0;
31    for (unsigned i = 0; i < NumStrings; i++)
32      Length += static_cast<StringLiteral *>(Strings[i])->getByteLength();
33    char *strBuf = new char [Length];
34    char *p = strBuf;
35    bool isWide = false;
36    for (unsigned i = 0; i < NumStrings; i++) {
37      S = static_cast<StringLiteral *>(Strings[i]);
38      if (S->isWide())
39        isWide = true;
40      memcpy(p, S->getStrData(), S->getByteLength());
41      p += S->getByteLength();
42      delete S;
43    }
44    S = new StringLiteral(strBuf, Length,
45                          isWide, Context.getPointerType(Context.CharTy),
46                          AtLoc, EndLoc);
47  }
48
49  if (CheckBuiltinCFStringArgument(S))
50    return true;
51
52  if (Context.getObjCConstantStringInterface().isNull()) {
53    // Initialize the constant string interface lazily. This assumes
54    // the NSConstantString interface is seen in this translation unit.
55    IdentifierInfo *NSIdent = &Context.Idents.get("NSConstantString");
56    Decl *IFace = LookupDecl(NSIdent, Decl::IDNS_Ordinary, TUScope);
57    ObjCInterfaceDecl *strIFace = dyn_cast_or_null<ObjCInterfaceDecl>(IFace);
58    if (strIFace)
59      Context.setObjCConstantStringInterface(strIFace);
60  }
61  QualType t = Context.getObjCConstantStringInterface();
62  // If there is no NSConstantString interface defined then treat constant
63  // strings as untyped objects and let the runtime figure it out later.
64  if (t == QualType()) {
65    t = Context.getObjCIdType();
66  } else {
67    t = Context.getPointerType(t);
68  }
69  return new ObjCStringLiteral(S, t, AtLoc);
70}
71
72Sema::ExprResult Sema::ParseObjCEncodeExpression(SourceLocation AtLoc,
73                                                 SourceLocation EncodeLoc,
74                                                 SourceLocation LParenLoc,
75                                                 TypeTy *Ty,
76                                                 SourceLocation RParenLoc) {
77  QualType EncodedType = QualType::getFromOpaquePtr(Ty);
78
79  QualType t = Context.getPointerType(Context.CharTy);
80  return new ObjCEncodeExpr(t, EncodedType, AtLoc, RParenLoc);
81}
82
83Sema::ExprResult Sema::ParseObjCSelectorExpression(Selector Sel,
84                                                   SourceLocation AtLoc,
85                                                   SourceLocation SelLoc,
86                                                   SourceLocation LParenLoc,
87                                                   SourceLocation RParenLoc) {
88  QualType t = Context.getObjCSelType();
89  return new ObjCSelectorExpr(t, Sel, AtLoc, RParenLoc);
90}
91
92Sema::ExprResult Sema::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId,
93                                                   SourceLocation AtLoc,
94                                                   SourceLocation ProtoLoc,
95                                                   SourceLocation LParenLoc,
96                                                   SourceLocation RParenLoc) {
97  ObjCProtocolDecl* PDecl = ObjCProtocols[ProtocolId];
98  if (!PDecl) {
99    Diag(ProtoLoc, diag::err_undeclared_protocol, ProtocolId->getName());
100    return true;
101  }
102
103  QualType t = Context.getObjCProtoType();
104  if (t.isNull())
105    return true;
106  t = Context.getPointerType(t);
107  return new ObjCProtocolExpr(t, PDecl, AtLoc, RParenLoc);
108}
109
110bool Sema::CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs,
111                                     Selector Sel, ObjCMethodDecl *Method,
112                                     const char *PrefixStr,
113                                     SourceLocation lbrac, SourceLocation rbrac,
114                                     QualType &ReturnType) {
115  if (!Method) {
116    // Apply default argument promotion as for (C99 6.5.2.2p6).
117    for (unsigned i = 0; i != NumArgs; i++)
118      DefaultArgumentPromotion(Args[i]);
119
120    Diag(lbrac, diag::warn_method_not_found, std::string(PrefixStr),
121         Sel.getName(), SourceRange(lbrac, rbrac));
122    ReturnType = Context.getObjCIdType();
123    return false;
124  } else {
125    ReturnType = Method->getResultType();
126  }
127
128  unsigned NumNamedArgs = Sel.getNumArgs();
129  assert(NumArgs >= NumNamedArgs && "Too few arguments for selector!");
130
131  bool anyIncompatibleArgs = false;
132  for (unsigned i = 0; i < NumNamedArgs; i++) {
133    Expr *argExpr = Args[i];
134    assert(argExpr && "CheckMessageArgumentTypes(): missing expression");
135
136    QualType lhsType = Method->getParamDecl(i)->getType();
137    QualType rhsType = argExpr->getType();
138
139    // If necessary, apply function/array conversion. C99 6.7.5.3p[7,8].
140    if (lhsType->isArrayType())
141      lhsType = Context.getArrayDecayedType(lhsType);
142    else if (lhsType->isFunctionType())
143      lhsType = Context.getPointerType(lhsType);
144
145    AssignConvertType Result =
146      CheckSingleAssignmentConstraints(lhsType, argExpr);
147    if (Args[i] != argExpr) // The expression was converted.
148      Args[i] = argExpr; // Make sure we store the converted expression.
149
150    anyIncompatibleArgs |=
151      DiagnoseAssignmentResult(Result, argExpr->getLocStart(), lhsType, rhsType,
152                               argExpr, "sending");
153  }
154
155  // Promote additional arguments to variadic methods.
156  if (Method->isVariadic()) {
157    for (unsigned i = NumNamedArgs; i < NumArgs; ++i)
158      DefaultArgumentPromotion(Args[i]);
159  } else {
160    // Check for extra arguments to non-variadic methods.
161    if (NumArgs != NumNamedArgs) {
162      Diag(Args[NumNamedArgs]->getLocStart(),
163           diag::err_typecheck_call_too_many_args,
164           Method->getSourceRange(),
165           SourceRange(Args[NumNamedArgs]->getLocStart(),
166                       Args[NumArgs-1]->getLocEnd()));
167    }
168  }
169
170  return anyIncompatibleArgs;
171}
172
173// ActOnClassMessage - used for both unary and keyword messages.
174// ArgExprs is optional - if it is present, the number of expressions
175// is obtained from Sel.getNumArgs().
176Sema::ExprResult Sema::ActOnClassMessage(
177  Scope *S,
178  IdentifierInfo *receiverName, Selector Sel,
179  SourceLocation lbrac, SourceLocation rbrac, ExprTy **Args, unsigned NumArgs)
180{
181  assert(receiverName && "missing receiver class name");
182
183  Expr **ArgExprs = reinterpret_cast<Expr **>(Args);
184  ObjCInterfaceDecl* ClassDecl = 0;
185  bool isSuper = false;
186
187  if (receiverName == SuperID && getCurMethodDecl()) {
188    isSuper = true;
189    ClassDecl = getCurMethodDecl()->getClassInterface()->getSuperClass();
190    if (!ClassDecl)
191      return Diag(lbrac, diag::error_no_super_class,
192                  getCurMethodDecl()->getClassInterface()->getName());
193    if (getCurMethodDecl()->isInstance()) {
194      QualType superTy = Context.getObjCInterfaceType(ClassDecl);
195      superTy = Context.getPointerType(superTy);
196      ExprResult ReceiverExpr = new PredefinedExpr(SourceLocation(), superTy,
197          PredefinedExpr::ObjCSuper);
198      // We are really in an instance method, redirect.
199      return ActOnInstanceMessage(ReceiverExpr.Val, Sel, lbrac, rbrac,
200                                  Args, NumArgs);
201    }
202    // We are sending a message to 'super' within a class method. Do nothing,
203    // the receiver will pass through as 'super' (how convenient:-).
204  } else
205    ClassDecl = getObjCInterfaceDecl(receiverName);
206
207  // The following code allows for the following GCC-ism:
208  //
209  //  typedef XCElementDisplayRect XCElementGraphicsRect;
210  //
211  //  @implementation XCRASlice
212  //  - whatever { // Note that XCElementGraphicsRect is a typedef name.
213  //    _sGraphicsDelegate =[[XCElementGraphicsRect alloc] init];
214  //  }
215  //
216  // If necessary, the following lookup could move to getObjCInterfaceDecl().
217  if (!ClassDecl) {
218    Decl *IDecl = LookupDecl(receiverName, Decl::IDNS_Ordinary, 0, false);
219    if (TypedefDecl *OCTD = dyn_cast_or_null<TypedefDecl>(IDecl)) {
220      const ObjCInterfaceType *OCIT;
221      OCIT = OCTD->getUnderlyingType()->getAsObjCInterfaceType();
222      if (OCIT)
223        ClassDecl = OCIT->getDecl();
224    }
225  }
226  assert(ClassDecl && "missing interface declaration");
227  ObjCMethodDecl *Method = 0;
228  QualType returnType;
229  Method = ClassDecl->lookupClassMethod(Sel);
230
231  // If we have an implementation in scope, check "private" methods.
232  if (!Method) {
233    if (ObjCImplementationDecl *ImpDecl =
234        ObjCImplementations[ClassDecl->getIdentifier()])
235      Method = ImpDecl->getClassMethod(Sel);
236
237    // Look through local category implementations associated with the class.
238    if (!Method) {
239      for (unsigned i = 0; i < ObjCCategoryImpls.size() && !Method; i++) {
240        if (ObjCCategoryImpls[i]->getClassInterface() == ClassDecl)
241          Method = ObjCCategoryImpls[i]->getClassMethod(Sel);
242      }
243    }
244  }
245  // Before we give up, check if the selector is an instance method.
246  if (!Method)
247    Method = ClassDecl->lookupInstanceMethod(Sel);
248
249  if (CheckMessageArgumentTypes(ArgExprs, NumArgs, Sel, Method, "+",
250                                lbrac, rbrac, returnType))
251    return true;
252
253  // If we have the ObjCInterfaceDecl* for the class that is receiving
254  // the message, use that to construct the ObjCMessageExpr.  Otherwise
255  // pass on the IdentifierInfo* for the class.
256  // FIXME: need to do a better job handling 'super' usage within a class
257  // For now, we simply pass the "super" identifier through (which isn't
258  // consistent with instance methods.
259  if (isSuper)
260    return new ObjCMessageExpr(receiverName, Sel, returnType, Method,
261                               lbrac, rbrac, ArgExprs, NumArgs);
262  else
263    return new ObjCMessageExpr(ClassDecl, Sel, returnType, Method,
264                               lbrac, rbrac, ArgExprs, NumArgs);
265}
266
267// ActOnInstanceMessage - used for both unary and keyword messages.
268// ArgExprs is optional - if it is present, the number of expressions
269// is obtained from Sel.getNumArgs().
270Sema::ExprResult Sema::ActOnInstanceMessage(ExprTy *receiver, Selector Sel,
271                                            SourceLocation lbrac,
272                                            SourceLocation rbrac,
273                                            ExprTy **Args, unsigned NumArgs) {
274  assert(receiver && "missing receiver expression");
275
276  Expr **ArgExprs = reinterpret_cast<Expr **>(Args);
277  Expr *RExpr = static_cast<Expr *>(receiver);
278  QualType returnType;
279
280  QualType ReceiverCType =
281    Context.getCanonicalType(RExpr->getType()).getUnqualifiedType();
282
283  // Handle messages to id.
284  if (ReceiverCType == Context.getCanonicalType(Context.getObjCIdType())) {
285    ObjCMethodDecl *Method = InstanceMethodPool[Sel].Method;
286    if (!Method)
287      Method = FactoryMethodPool[Sel].Method;
288    if (CheckMessageArgumentTypes(ArgExprs, NumArgs, Sel, Method, "-",
289                                  lbrac, rbrac, returnType))
290      return true;
291    return new ObjCMessageExpr(RExpr, Sel, returnType, Method, lbrac, rbrac,
292                               ArgExprs, NumArgs);
293  }
294
295  // Handle messages to Class.
296  if (ReceiverCType == Context.getCanonicalType(Context.getObjCClassType())) {
297    ObjCMethodDecl *Method = 0;
298    if (ObjCMethodDecl *CurMeth = getCurMethodDecl()) {
299      // If we have an implementation in scope, check "private" methods.
300      if (ObjCInterfaceDecl *ClassDecl = CurMeth->getClassInterface())
301        if (ObjCImplementationDecl *ImpDecl =
302              ObjCImplementations[ClassDecl->getIdentifier()])
303          Method = ImpDecl->getClassMethod(Sel);
304    }
305    if (!Method)
306      Method = FactoryMethodPool[Sel].Method;
307    if (!Method)
308      Method = InstanceMethodPool[Sel].Method;
309    if (CheckMessageArgumentTypes(ArgExprs, NumArgs, Sel, Method, "-",
310                                  lbrac, rbrac, returnType))
311      return true;
312    return new ObjCMessageExpr(RExpr, Sel, returnType, Method, lbrac, rbrac,
313                               ArgExprs, NumArgs);
314  }
315
316  ObjCMethodDecl *Method = 0;
317  ObjCInterfaceDecl* ClassDecl = 0;
318
319  // We allow sending a message to a qualified ID ("id<foo>"), which is ok as
320  // long as one of the protocols implements the selector (if not, warn).
321  if (ObjCQualifiedIdType *QIT = dyn_cast<ObjCQualifiedIdType>(ReceiverCType)) {
322    // Search protocols
323    for (unsigned i = 0; i < QIT->getNumProtocols(); i++) {
324      ObjCProtocolDecl *PDecl = QIT->getProtocols(i);
325      if (PDecl && (Method = PDecl->lookupInstanceMethod(Sel)))
326        break;
327    }
328    if (!Method)
329      Diag(lbrac, diag::warn_method_not_found_in_protocol,
330           std::string("-"), Sel.getName(),
331           RExpr->getSourceRange());
332  } else if (const ObjCInterfaceType *OCIReceiver =
333                ReceiverCType->getAsPointerToObjCInterfaceType()) {
334    // We allow sending a message to a pointer to an interface (an object).
335
336    ClassDecl = OCIReceiver->getDecl();
337    // FIXME: consider using InstanceMethodPool, since it will be faster
338    // than the following method (which can do *many* linear searches). The
339    // idea is to add class info to InstanceMethodPool.
340    Method = ClassDecl->lookupInstanceMethod(Sel);
341
342    if (!Method) {
343      // Search protocol qualifiers.
344      for (ObjCQualifiedIdType::qual_iterator QI = OCIReceiver->qual_begin(),
345           E = OCIReceiver->qual_end(); QI != E; ++QI) {
346        if ((Method = (*QI)->lookupInstanceMethod(Sel)))
347          break;
348      }
349    }
350
351    if (!Method && !OCIReceiver->qual_empty())
352      Diag(lbrac, diag::warn_method_not_found_in_protocol,
353           std::string("-"), Sel.getName(),
354           SourceRange(lbrac, rbrac));
355  } else {
356    Diag(lbrac, diag::error_bad_receiver_type,
357         RExpr->getType().getAsString(), RExpr->getSourceRange());
358    return true;
359  }
360
361  if (!Method) {
362    // If we have an implementation in scope, check "private" methods.
363    if (ClassDecl)
364      if (ObjCImplementationDecl *ImpDecl =
365            ObjCImplementations[ClassDecl->getIdentifier()])
366        Method = ImpDecl->getInstanceMethod(Sel);
367        // If we still haven't found a method, look in the global pool. This
368        // behavior isn't very desirable, however we need it for GCC
369        // compatibility.
370        if (!Method)
371          Method = InstanceMethodPool[Sel].Method;
372  }
373  if (CheckMessageArgumentTypes(ArgExprs, NumArgs, Sel, Method, "-",
374                                lbrac, rbrac, returnType))
375    return true;
376  return new ObjCMessageExpr(RExpr, Sel, returnType, Method, lbrac, rbrac,
377                             ArgExprs, NumArgs);
378}
379
380//===----------------------------------------------------------------------===//
381// ObjCQualifiedIdTypesAreCompatible - Compatibility testing for qualified id's.
382//===----------------------------------------------------------------------===//
383
384/// ProtocolCompatibleWithProtocol - return 'true' if 'lProto' is in the
385/// inheritance hierarchy of 'rProto'.
386static bool ProtocolCompatibleWithProtocol(ObjCProtocolDecl *lProto,
387                                           ObjCProtocolDecl *rProto) {
388  if (lProto == rProto)
389    return true;
390  for (ObjCProtocolDecl::protocol_iterator PI = rProto->protocol_begin(),
391       E = rProto->protocol_end(); PI != E; ++PI)
392    if (ProtocolCompatibleWithProtocol(lProto, *PI))
393      return true;
394  return false;
395}
396
397/// ClassImplementsProtocol - Checks that 'lProto' protocol
398/// has been implemented in IDecl class, its super class or categories (if
399/// lookupCategory is true).
400static bool ClassImplementsProtocol(ObjCProtocolDecl *lProto,
401                                    ObjCInterfaceDecl *IDecl,
402                                    bool lookupCategory,
403                                    bool RHSIsQualifiedID = false) {
404
405  // 1st, look up the class.
406  const ObjCList<ObjCProtocolDecl> &Protocols =
407    IDecl->getReferencedProtocols();
408
409  for (ObjCList<ObjCProtocolDecl>::iterator PI = Protocols.begin(),
410       E = Protocols.end(); PI != E; ++PI) {
411    if (ProtocolCompatibleWithProtocol(lProto, *PI))
412      return true;
413    // This is dubious and is added to be compatible with gcc.
414    // In gcc, it is also allowed assigning a protocol-qualified 'id'
415    // type to a LHS object when protocol in qualified LHS is in list
416    // of protocols in the rhs 'id' object. This IMO, should be a bug.
417    // FIXME: Treat this as an extension, and flag this as an error when
418    //  GCC extensions are not enabled.
419    if (RHSIsQualifiedID && ProtocolCompatibleWithProtocol(*PI, lProto))
420      return true;
421  }
422
423  // 2nd, look up the category.
424  if (lookupCategory)
425    for (ObjCCategoryDecl *CDecl = IDecl->getCategoryList(); CDecl;
426         CDecl = CDecl->getNextClassCategory()) {
427      for (ObjCCategoryDecl::protocol_iterator PI = CDecl->protocol_begin(),
428           E = CDecl->protocol_end(); PI != E; ++PI)
429        if (ProtocolCompatibleWithProtocol(lProto, *PI))
430          return true;
431    }
432
433  // 3rd, look up the super class(s)
434  if (IDecl->getSuperClass())
435    return
436      ClassImplementsProtocol(lProto, IDecl->getSuperClass(), lookupCategory,
437                              RHSIsQualifiedID);
438
439  return false;
440}
441
442/// ObjCQualifiedIdTypesAreCompatible - We know that one of lhs/rhs is an
443/// ObjCQualifiedIDType.
444bool Sema::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs,
445                                             bool compare) {
446  // Allow id<P..> and an 'id' or void* type in all cases.
447  if (const PointerType *PT = lhs->getAsPointerType()) {
448    QualType PointeeTy = PT->getPointeeType();
449    if (Context.isObjCIdType(PointeeTy) || PointeeTy->isVoidType())
450      return true;
451  } else if (const PointerType *PT = rhs->getAsPointerType()) {
452    QualType PointeeTy = PT->getPointeeType();
453    if (Context.isObjCIdType(PointeeTy) || PointeeTy->isVoidType())
454      return true;
455  }
456
457  if (const ObjCQualifiedIdType *lhsQID = lhs->getAsObjCQualifiedIdType()) {
458    const ObjCQualifiedIdType *rhsQID = rhs->getAsObjCQualifiedIdType();
459    const ObjCQualifiedInterfaceType *rhsQI = 0;
460    QualType rtype;
461
462    if (!rhsQID) {
463      // Not comparing two ObjCQualifiedIdType's?
464      if (!rhs->isPointerType()) return false;
465
466      rtype = rhs->getAsPointerType()->getPointeeType();
467      rhsQI = rtype->getAsObjCQualifiedInterfaceType();
468      if (rhsQI == 0) {
469        // If the RHS is a unqualified interface pointer "NSString*",
470        // make sure we check the class hierarchy.
471        if (const ObjCInterfaceType *IT = rtype->getAsObjCInterfaceType()) {
472          ObjCInterfaceDecl *rhsID = IT->getDecl();
473          for (unsigned i = 0; i != lhsQID->getNumProtocols(); ++i) {
474            // when comparing an id<P> on lhs with a static type on rhs,
475            // see if static class implements all of id's protocols, directly or
476            // through its super class and categories.
477            if (!ClassImplementsProtocol(lhsQID->getProtocols(i), rhsID, true))
478              return false;
479          }
480          return true;
481        }
482      }
483    }
484
485    ObjCQualifiedIdType::qual_iterator RHSProtoI, RHSProtoE;
486    if (rhsQI) { // We have a qualified interface (e.g. "NSObject<Proto> *").
487      RHSProtoI = rhsQI->qual_begin();
488      RHSProtoE = rhsQI->qual_end();
489    } else if (rhsQID) { // We have a qualified id (e.g. "id<Proto> *").
490      RHSProtoI = rhsQID->qual_begin();
491      RHSProtoE = rhsQID->qual_end();
492    } else {
493      return false;
494    }
495
496    for (unsigned i =0; i < lhsQID->getNumProtocols(); i++) {
497      ObjCProtocolDecl *lhsProto = lhsQID->getProtocols(i);
498      bool match = false;
499
500      // when comparing an id<P> on lhs with a static type on rhs,
501      // see if static class implements all of id's protocols, directly or
502      // through its super class and categories.
503      for (; RHSProtoI != RHSProtoE; ++RHSProtoI) {
504        ObjCProtocolDecl *rhsProto = *RHSProtoI;
505        if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) ||
506            compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto)) {
507          match = true;
508          break;
509        }
510      }
511      if (rhsQI) {
512        // If the RHS is a qualified interface pointer "NSString<P>*",
513        // make sure we check the class hierarchy.
514        if (const ObjCInterfaceType *IT = rtype->getAsObjCInterfaceType()) {
515          ObjCInterfaceDecl *rhsID = IT->getDecl();
516          for (unsigned i = 0; i != lhsQID->getNumProtocols(); ++i) {
517            // when comparing an id<P> on lhs with a static type on rhs,
518            // see if static class implements all of id's protocols, directly or
519            // through its super class and categories.
520            if (ClassImplementsProtocol(lhsQID->getProtocols(i), rhsID, true)) {
521              match = true;
522              break;
523            }
524          }
525        }
526      }
527      if (!match)
528        return false;
529    }
530
531    return true;
532  }
533
534  const ObjCQualifiedIdType *rhsQID = rhs->getAsObjCQualifiedIdType();
535  assert(rhsQID && "One of the LHS/RHS should be id<x>");
536
537  if (!lhs->isPointerType())
538    return false;
539
540  QualType ltype = lhs->getAsPointerType()->getPointeeType();
541  if (const ObjCQualifiedInterfaceType *lhsQI =
542         ltype->getAsObjCQualifiedInterfaceType()) {
543    ObjCQualifiedIdType::qual_iterator LHSProtoI = lhsQI->qual_begin();
544    ObjCQualifiedIdType::qual_iterator LHSProtoE = lhsQI->qual_end();
545    for (; LHSProtoI != LHSProtoE; ++LHSProtoI) {
546      bool match = false;
547      ObjCProtocolDecl *lhsProto = *LHSProtoI;
548      for (unsigned j = 0; j < rhsQID->getNumProtocols(); j++) {
549        ObjCProtocolDecl *rhsProto = rhsQID->getProtocols(j);
550        if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) ||
551            compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto)) {
552          match = true;
553          break;
554        }
555      }
556      if (!match)
557        return false;
558    }
559    return true;
560  }
561
562  if (const ObjCInterfaceType *IT = ltype->getAsObjCInterfaceType()) {
563    // for static type vs. qualified 'id' type, check that class implements
564    // all of 'id's protocols.
565    ObjCInterfaceDecl *lhsID = IT->getDecl();
566    for (unsigned j = 0; j < rhsQID->getNumProtocols(); j++) {
567      ObjCProtocolDecl *rhsProto = rhsQID->getProtocols(j);
568      if (!ClassImplementsProtocol(rhsProto, lhsID, compare, true))
569        return false;
570    }
571    return true;
572  }
573  return false;
574}
575
576