diff options
Diffstat (limited to 'lib/Sema/SemaDeclObjC.cpp')
-rw-r--r-- | lib/Sema/SemaDeclObjC.cpp | 540 |
1 files changed, 447 insertions, 93 deletions
diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index de9097e..aa8152b 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -16,14 +16,96 @@ #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" +#include "clang/AST/ASTConsumer.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceManager.h" #include "clang/Sema/DeclSpec.h" #include "llvm/ADT/DenseSet.h" using namespace clang; +/// Check whether the given method, which must be in the 'init' +/// family, is a valid member of that family. +/// +/// \param receiverTypeIfCall - if null, check this as if declaring it; +/// if non-null, check this as if making a call to it with the given +/// receiver type +/// +/// \return true to indicate that there was an error and appropriate +/// actions were taken +bool Sema::checkInitMethod(ObjCMethodDecl *method, + QualType receiverTypeIfCall) { + if (method->isInvalidDecl()) return true; + + // This castAs is safe: methods that don't return an object + // pointer won't be inferred as inits and will reject an explicit + // objc_method_family(init). + + // We ignore protocols here. Should we? What about Class? + + const ObjCObjectType *result = method->getResultType() + ->castAs<ObjCObjectPointerType>()->getObjectType(); + + if (result->isObjCId()) { + return false; + } else if (result->isObjCClass()) { + // fall through: always an error + } else { + ObjCInterfaceDecl *resultClass = result->getInterface(); + assert(resultClass && "unexpected object type!"); + + // It's okay for the result type to still be a forward declaration + // if we're checking an interface declaration. + if (resultClass->isForwardDecl()) { + if (receiverTypeIfCall.isNull() && + !isa<ObjCImplementationDecl>(method->getDeclContext())) + return false; + + // Otherwise, we try to compare class types. + } else { + // If this method was declared in a protocol, we can't check + // anything unless we have a receiver type that's an interface. + const ObjCInterfaceDecl *receiverClass = 0; + if (isa<ObjCProtocolDecl>(method->getDeclContext())) { + if (receiverTypeIfCall.isNull()) + return false; + + receiverClass = receiverTypeIfCall->castAs<ObjCObjectPointerType>() + ->getInterfaceDecl(); + + // This can be null for calls to e.g. id<Foo>. + if (!receiverClass) return false; + } else { + receiverClass = method->getClassInterface(); + assert(receiverClass && "method not associated with a class!"); + } + + // If either class is a subclass of the other, it's fine. + if (receiverClass->isSuperClassOf(resultClass) || + resultClass->isSuperClassOf(receiverClass)) + return false; + } + } + + SourceLocation loc = method->getLocation(); + + // If we're in a system header, and this is not a call, just make + // the method unusable. + if (receiverTypeIfCall.isNull() && getSourceManager().isInSystemHeader(loc)) { + method->addAttr(new (Context) UnavailableAttr(loc, Context, + "init method returns a type unrelated to its receiver type")); + return true; + } + + // Otherwise, it's an error. + Diag(loc, diag::err_arc_init_method_unrelated_result_type); + method->setInvalidDecl(); + return true; +} + bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, const ObjCMethodDecl *Overridden, bool IsImplementation) { @@ -36,7 +118,7 @@ bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, QualType ResultType = NewMethod->getResultType(); SourceRange ResultTypeRange; if (const TypeSourceInfo *ResultTypeInfo - = NewMethod->getResultTypeSourceInfo()) + = NewMethod->getResultTypeSourceInfo()) ResultTypeRange = ResultTypeInfo->getTypeLoc().getSourceRange(); // Figure out which class this method is part of, if any. @@ -73,7 +155,15 @@ bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, return false; } - +/// \brief Check for consistency between a given method declaration and the +/// methods it overrides within the class hierarchy. +/// +/// This method walks the inheritance hierarchy starting at the given +/// declaration context (\p DC), invoking Sema::CheckObjCMethodOverride() with +/// the given new method (\p NewMethod) and any method it directly overrides +/// in the hierarchy. Sema::CheckObjCMethodOverride() is responsible for +/// checking consistency, e.g., among return types for methods that return a +/// related result type. static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, DeclContext *DC, bool SkipCurrent = true) { @@ -99,10 +189,10 @@ static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, if (CheckObjCMethodOverrides(S, NewMethod, Category, false)) return true; } - + // Look through protocols. for (ObjCList<ObjCProtocolDecl>::iterator I = Class->protocol_begin(), - IEnd = Class->protocol_end(); + IEnd = Class->protocol_end(); I != IEnd; ++I) if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) return true; @@ -115,7 +205,7 @@ static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(DC)) { // Look through protocols. for (ObjCList<ObjCProtocolDecl>::iterator I = Category->protocol_begin(), - IEnd = Category->protocol_end(); + IEnd = Category->protocol_end(); I != IEnd; ++I) if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) return true; @@ -126,7 +216,7 @@ static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(DC)) { // Look through protocols. for (ObjCList<ObjCProtocolDecl>::iterator I = Protocol->protocol_begin(), - IEnd = Protocol->protocol_end(); + IEnd = Protocol->protocol_end(); I != IEnd; ++I) if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) return true; @@ -141,13 +231,13 @@ bool Sema::CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, DeclContext *DC) { if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Class); - + if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Category); - + if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Protocol); - + if (ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Impl->getClassInterface()); @@ -159,6 +249,55 @@ bool Sema::CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, return ::CheckObjCMethodOverrides(*this, NewMethod, CurContext); } +/// \brief Check a method declaration for compatibility with the Objective-C +/// ARC conventions. +static bool CheckARCMethodDecl(Sema &S, ObjCMethodDecl *method) { + ObjCMethodFamily family = method->getMethodFamily(); + switch (family) { + case OMF_None: + case OMF_dealloc: + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_retainCount: + case OMF_self: + return false; + + case OMF_init: + // If the method doesn't obey the init rules, don't bother annotating it. + if (S.checkInitMethod(method, QualType())) + return true; + + method->addAttr(new (S.Context) NSConsumesSelfAttr(SourceLocation(), + S.Context)); + + // Don't add a second copy of this attribute, but otherwise don't + // let it be suppressed. + if (method->hasAttr<NSReturnsRetainedAttr>()) + return false; + break; + + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + if (method->hasAttr<NSReturnsRetainedAttr>() || + method->hasAttr<NSReturnsNotRetainedAttr>() || + method->hasAttr<NSReturnsAutoreleasedAttr>()) + return false; + break; + + case OMF_performSelector: + // we don't annotate performSelector's + return true; + + } + + method->addAttr(new (S.Context) NSReturnsRetainedAttr(SourceLocation(), + S.Context)); + return false; +} + static void DiagnoseObjCImplementedDeprecations(Sema &S, NamedDecl *ND, SourceLocation ImplLoc, @@ -212,6 +351,31 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) { if ((*PI)->getIdentifier()) PushOnScopeChains(*PI, FnBodyScope); } + + // In ARC, disallow definition of retain/release/autorelease/retainCount + if (getLangOptions().ObjCAutoRefCount) { + switch (MDecl->getMethodFamily()) { + case OMF_retain: + case OMF_retainCount: + case OMF_release: + case OMF_autorelease: + Diag(MDecl->getLocation(), diag::err_arc_illegal_method_def) + << MDecl->getSelector(); + break; + + case OMF_None: + case OMF_dealloc: + case OMF_alloc: + case OMF_init: + case OMF_mutableCopy: + case OMF_copy: + case OMF_new: + case OMF_self: + case OMF_performSelector: + break; + } + } + // Warn on implementating deprecated methods under // -Wdeprecated-implementations flag. if (ObjCInterfaceDecl *IC = MDecl->getClassInterface()) @@ -284,9 +448,10 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, if (!PrevDecl) { // Try to correct for a typo in the superclass name. - LookupResult R(*this, SuperName, SuperLoc, LookupOrdinaryName); - if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) && - (PrevDecl = R.getAsSingle<ObjCInterfaceDecl>())) { + TypoCorrection Corrected = CorrectTypo( + DeclarationNameInfo(SuperName, SuperLoc), LookupOrdinaryName, TUScope, + NULL, NULL, false, CTC_NoKeywords); + if ((PrevDecl = Corrected.getCorrectionDeclAs<ObjCInterfaceDecl>())) { Diag(SuperLoc, diag::err_undef_superclass_suggest) << SuperName << ClassName << PrevDecl->getDeclName(); Diag(PrevDecl->getLocation(), diag::note_previous_decl) @@ -333,10 +498,13 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, if (!SuperClassDecl) Diag(SuperLoc, diag::err_undef_superclass) << SuperName << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); - else if (SuperClassDecl->isForwardDecl()) - Diag(SuperLoc, diag::err_undef_superclass) + else if (SuperClassDecl->isForwardDecl()) { + Diag(SuperLoc, diag::err_forward_superclass) << SuperClassDecl->getDeclName() << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); + Diag(SuperClassDecl->getLocation(), diag::note_forward_class); + SuperClassDecl = 0; + } } IDecl->setSuperClass(SuperClassDecl); IDecl->setSuperClassLoc(SuperLoc); @@ -494,12 +662,12 @@ Sema::FindProtocolDeclaration(bool WarnOnDeclarations, ObjCProtocolDecl *PDecl = LookupProtocol(ProtocolId[i].first, ProtocolId[i].second); if (!PDecl) { - LookupResult R(*this, ProtocolId[i].first, ProtocolId[i].second, - LookupObjCProtocolName); - if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) && - (PDecl = R.getAsSingle<ObjCProtocolDecl>())) { + TypoCorrection Corrected = CorrectTypo( + DeclarationNameInfo(ProtocolId[i].first, ProtocolId[i].second), + LookupObjCProtocolName, TUScope, NULL, NULL, false, CTC_NoKeywords); + if ((PDecl = Corrected.getCorrectionDeclAs<ObjCProtocolDecl>())) { Diag(ProtocolId[i].second, diag::err_undeclared_protocol_suggest) - << ProtocolId[i].first << R.getLookupName(); + << ProtocolId[i].first << Corrected.getCorrection(); Diag(PDecl->getLocation(), diag::note_previous_decl) << PDecl->getDeclName(); } @@ -736,20 +904,20 @@ Decl *Sema::ActOnStartClassImplementation( } else { // We did not find anything with the name ClassName; try to correct for // typos in the class name. - LookupResult R(*this, ClassName, ClassLoc, LookupOrdinaryName); - if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) && - (IDecl = R.getAsSingle<ObjCInterfaceDecl>())) { + TypoCorrection Corrected = CorrectTypo( + DeclarationNameInfo(ClassName, ClassLoc), LookupOrdinaryName, TUScope, + NULL, NULL, false, CTC_NoKeywords); + if ((IDecl = Corrected.getCorrectionDeclAs<ObjCInterfaceDecl>())) { // Suggest the (potentially) correct interface name. However, put the // fix-it hint itself in a separate note, since changing the name in // the warning would make the fix-it change semantics.However, don't // provide a code-modification hint or use the typo name for recovery, // because this is just a warning. The program may actually be correct. + DeclarationName CorrectedName = Corrected.getCorrection(); Diag(ClassLoc, diag::warn_undef_interface_suggest) - << ClassName << R.getLookupName(); - Diag(IDecl->getLocation(), diag::note_previous_decl) - << R.getLookupName() - << FixItHint::CreateReplacement(ClassLoc, - R.getLookupName().getAsString()); + << ClassName << CorrectedName; + Diag(IDecl->getLocation(), diag::note_previous_decl) << CorrectedName + << FixItHint::CreateReplacement(ClassLoc, CorrectedName.getAsString()); IDecl = 0; } else { Diag(ClassLoc, diag::warn_undef_interface) << ClassName; @@ -915,6 +1083,9 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, void Sema::WarnUndefinedMethod(SourceLocation ImpLoc, ObjCMethodDecl *method, bool &IncompleteImpl, unsigned DiagID) { + // No point warning no definition of method which is 'unavailable'. + if (method->hasAttr<UnavailableAttr>()) + return; if (!IncompleteImpl) { Diag(ImpLoc, diag::warn_incomplete_impl); IncompleteImpl = true; @@ -1091,11 +1262,84 @@ static void CheckMethodOverrideParam(Sema &S, S.Diag(IfaceVar->getLocation(), diag::note_previous_definition) << getTypeRange(IfaceVar->getTypeSourceInfo()); } - + +/// In ARC, check whether the conventional meanings of the two methods +/// match. If they don't, it's a hard error. +static bool checkMethodFamilyMismatch(Sema &S, ObjCMethodDecl *impl, + ObjCMethodDecl *decl) { + ObjCMethodFamily implFamily = impl->getMethodFamily(); + ObjCMethodFamily declFamily = decl->getMethodFamily(); + if (implFamily == declFamily) return false; + + // Since conventions are sorted by selector, the only possibility is + // that the types differ enough to cause one selector or the other + // to fall out of the family. + assert(implFamily == OMF_None || declFamily == OMF_None); + + // No further diagnostics required on invalid declarations. + if (impl->isInvalidDecl() || decl->isInvalidDecl()) return true; + + const ObjCMethodDecl *unmatched = impl; + ObjCMethodFamily family = declFamily; + unsigned errorID = diag::err_arc_lost_method_convention; + unsigned noteID = diag::note_arc_lost_method_convention; + if (declFamily == OMF_None) { + unmatched = decl; + family = implFamily; + errorID = diag::err_arc_gained_method_convention; + noteID = diag::note_arc_gained_method_convention; + } + + // Indexes into a %select clause in the diagnostic. + enum FamilySelector { + F_alloc, F_copy, F_mutableCopy = F_copy, F_init, F_new + }; + FamilySelector familySelector = FamilySelector(); + + switch (family) { + case OMF_None: llvm_unreachable("logic error, no method convention"); + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_dealloc: + case OMF_retainCount: + case OMF_self: + case OMF_performSelector: + // Mismatches for these methods don't change ownership + // conventions, so we don't care. + return false; + + case OMF_init: familySelector = F_init; break; + case OMF_alloc: familySelector = F_alloc; break; + case OMF_copy: familySelector = F_copy; break; + case OMF_mutableCopy: familySelector = F_mutableCopy; break; + case OMF_new: familySelector = F_new; break; + } + + enum ReasonSelector { R_NonObjectReturn, R_UnrelatedReturn }; + ReasonSelector reasonSelector; + + // The only reason these methods don't fall within their families is + // due to unusual result types. + if (unmatched->getResultType()->isObjCObjectPointerType()) { + reasonSelector = R_UnrelatedReturn; + } else { + reasonSelector = R_NonObjectReturn; + } + + S.Diag(impl->getLocation(), errorID) << familySelector << reasonSelector; + S.Diag(decl->getLocation(), noteID) << familySelector << reasonSelector; + + return true; +} void Sema::WarnConflictingTypedMethods(ObjCMethodDecl *ImpMethodDecl, ObjCMethodDecl *MethodDecl, bool IsProtocolMethodDecl) { + if (getLangOptions().ObjCAutoRefCount && + checkMethodFamilyMismatch(*this, ImpMethodDecl, MethodDecl)) + return; + CheckMethodOverrideReturn(*this, ImpMethodDecl, MethodDecl, IsProtocolMethodDecl); @@ -1204,7 +1448,7 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, CheckProtocolMethodDefs(ImpLoc, *PI, IncompleteImpl, InsMap, ClsMap, IDecl); } -/// MatchAllMethodDeclarations - Check methods declaraed in interface or +/// MatchAllMethodDeclarations - Check methods declared in interface /// or protocol against those declared in their implementations. /// void Sema::MatchAllMethodDeclarations(const llvm::DenseSet<Selector> &InsMap, @@ -1422,48 +1666,117 @@ Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, return CDecl; } +static bool tryMatchRecordTypes(ASTContext &Context, + Sema::MethodMatchStrategy strategy, + const Type *left, const Type *right); + +static bool matchTypes(ASTContext &Context, Sema::MethodMatchStrategy strategy, + QualType leftQT, QualType rightQT) { + const Type *left = + Context.getCanonicalType(leftQT).getUnqualifiedType().getTypePtr(); + const Type *right = + Context.getCanonicalType(rightQT).getUnqualifiedType().getTypePtr(); + + if (left == right) return true; + + // If we're doing a strict match, the types have to match exactly. + if (strategy == Sema::MMS_strict) return false; + + if (left->isIncompleteType() || right->isIncompleteType()) return false; + + // Otherwise, use this absurdly complicated algorithm to try to + // validate the basic, low-level compatibility of the two types. + + // As a minimum, require the sizes and alignments to match. + if (Context.getTypeInfo(left) != Context.getTypeInfo(right)) + return false; + + // Consider all the kinds of non-dependent canonical types: + // - functions and arrays aren't possible as return and parameter types + + // - vector types of equal size can be arbitrarily mixed + if (isa<VectorType>(left)) return isa<VectorType>(right); + if (isa<VectorType>(right)) return false; + + // - references should only match references of identical type + // - structs, unions, and Objective-C objects must match more-or-less + // exactly + // - everything else should be a scalar + if (!left->isScalarType() || !right->isScalarType()) + return tryMatchRecordTypes(Context, strategy, left, right); + + // Make scalars agree in kind, except count bools as chars. + Type::ScalarTypeKind leftSK = left->getScalarTypeKind(); + Type::ScalarTypeKind rightSK = right->getScalarTypeKind(); + if (leftSK == Type::STK_Bool) leftSK = Type::STK_Integral; + if (rightSK == Type::STK_Bool) rightSK = Type::STK_Integral; + + // Note that data member pointers and function member pointers don't + // intermix because of the size differences. + + return (leftSK == rightSK); +} + +static bool tryMatchRecordTypes(ASTContext &Context, + Sema::MethodMatchStrategy strategy, + const Type *lt, const Type *rt) { + assert(lt && rt && lt != rt); + + if (!isa<RecordType>(lt) || !isa<RecordType>(rt)) return false; + RecordDecl *left = cast<RecordType>(lt)->getDecl(); + RecordDecl *right = cast<RecordType>(rt)->getDecl(); + + // Require union-hood to match. + if (left->isUnion() != right->isUnion()) return false; + + // Require an exact match if either is non-POD. + if ((isa<CXXRecordDecl>(left) && !cast<CXXRecordDecl>(left)->isPOD()) || + (isa<CXXRecordDecl>(right) && !cast<CXXRecordDecl>(right)->isPOD())) + return false; + + // Require size and alignment to match. + if (Context.getTypeInfo(lt) != Context.getTypeInfo(rt)) return false; + + // Require fields to match. + RecordDecl::field_iterator li = left->field_begin(), le = left->field_end(); + RecordDecl::field_iterator ri = right->field_begin(), re = right->field_end(); + for (; li != le && ri != re; ++li, ++ri) { + if (!matchTypes(Context, strategy, li->getType(), ri->getType())) + return false; + } + return (li == le && ri == re); +} /// MatchTwoMethodDeclarations - Checks that two methods have matching type and /// returns true, or false, accordingly. /// TODO: Handle protocol list; such as id<p1,p2> in type comparisons -bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *Method, - const ObjCMethodDecl *PrevMethod, - bool matchBasedOnSizeAndAlignment, - bool matchBasedOnStrictEqulity) { - QualType T1 = Context.getCanonicalType(Method->getResultType()); - QualType T2 = Context.getCanonicalType(PrevMethod->getResultType()); - - if (T1 != T2) { - // The result types are different. - if (!matchBasedOnSizeAndAlignment || matchBasedOnStrictEqulity) - return false; - // Incomplete types don't have a size and alignment. - if (T1->isIncompleteType() || T2->isIncompleteType()) - return false; - // Check is based on size and alignment. - if (Context.getTypeInfo(T1) != Context.getTypeInfo(T2)) - return false; - } +bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left, + const ObjCMethodDecl *right, + MethodMatchStrategy strategy) { + if (!matchTypes(Context, strategy, + left->getResultType(), right->getResultType())) + return false; - ObjCMethodDecl::param_iterator ParamI = Method->param_begin(), - E = Method->param_end(); - ObjCMethodDecl::param_iterator PrevI = PrevMethod->param_begin(); + if (getLangOptions().ObjCAutoRefCount && + (left->hasAttr<NSReturnsRetainedAttr>() + != right->hasAttr<NSReturnsRetainedAttr>() || + left->hasAttr<NSConsumesSelfAttr>() + != right->hasAttr<NSConsumesSelfAttr>())) + return false; - for (; ParamI != E; ++ParamI, ++PrevI) { - assert(PrevI != PrevMethod->param_end() && "Param mismatch"); - T1 = Context.getCanonicalType((*ParamI)->getType()); - T2 = Context.getCanonicalType((*PrevI)->getType()); - if (T1 != T2) { - // The result types are different. - if (!matchBasedOnSizeAndAlignment || matchBasedOnStrictEqulity) - return false; - // Incomplete types don't have a size and alignment. - if (T1->isIncompleteType() || T2->isIncompleteType()) - return false; - // Check is based on size and alignment. - if (Context.getTypeInfo(T1) != Context.getTypeInfo(T2)) - return false; - } + ObjCMethodDecl::param_iterator + li = left->param_begin(), le = left->param_end(), ri = right->param_begin(); + + for (; li != le; ++li, ++ri) { + assert(ri != right->param_end() && "Param mismatch"); + ParmVarDecl *lparm = *li, *rparm = *ri; + + if (!matchTypes(Context, strategy, lparm->getType(), rparm->getType())) + return false; + + if (getLangOptions().ObjCAutoRefCount && + lparm->hasAttr<NSConsumedAttr>() != rparm->hasAttr<NSConsumedAttr>()) + return false; } return true; } @@ -1505,8 +1818,10 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, // We've seen a method with this name, see if we have already seen this type // signature. - for (ObjCMethodList *List = &Entry; List; List = List->Next) - if (MatchTwoMethodDeclarations(Method, List->Method)) { + for (ObjCMethodList *List = &Entry; List; List = List->Next) { + bool match = MatchTwoMethodDeclarations(Method, List->Method); + + if (match) { ObjCMethodDecl *PrevObjCMethod = List->Method; PrevObjCMethod->setDefined(impl); // If a method is deprecated, push it in the global pool. @@ -1523,6 +1838,7 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, } return; } + } // We have a new signature for an existing method - add it. // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". @@ -1530,6 +1846,25 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, Entry.Next = new (Mem) ObjCMethodList(Method, Entry.Next); } +/// Determines if this is an "acceptable" loose mismatch in the global +/// method pool. This exists mostly as a hack to get around certain +/// global mismatches which we can't afford to make warnings / errors. +/// Really, what we want is a way to take a method out of the global +/// method pool. +static bool isAcceptableMethodMismatch(ObjCMethodDecl *chosen, + ObjCMethodDecl *other) { + if (!chosen->isInstanceMethod()) + return false; + + Selector sel = chosen->getSelector(); + if (!sel.isUnarySelector() || sel.getNameForSlot(0) != "length") + return false; + + // Don't complain about mismatches for -length if the method we + // chose has an integral result type. + return (chosen->getResultType()->isIntegerType()); +} + ObjCMethodDecl *Sema::LookupMethodInGlobalPool(Selector Sel, SourceRange R, bool receiverIdOrClass, bool warn, bool instance) { @@ -1543,32 +1878,52 @@ ObjCMethodDecl *Sema::LookupMethodInGlobalPool(Selector Sel, SourceRange R, ObjCMethodList &MethList = instance ? Pos->second.first : Pos->second.second; - bool strictSelectorMatch = receiverIdOrClass && warn && - (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl, - R.getBegin()) != - Diagnostic::Ignored); if (warn && MethList.Method && MethList.Next) { - bool issueWarning = false; + bool issueDiagnostic = false, issueError = false; + + // We support a warning which complains about *any* difference in + // method signature. + bool strictSelectorMatch = + (receiverIdOrClass && warn && + (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl, + R.getBegin()) != + Diagnostic::Ignored)); if (strictSelectorMatch) for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) { - // This checks if the methods differ in type mismatch. - if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, false, true)) - issueWarning = true; + if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, + MMS_strict)) { + issueDiagnostic = true; + break; + } } - if (!issueWarning) + // If we didn't see any strict differences, we won't see any loose + // differences. In ARC, however, we also need to check for loose + // mismatches, because most of them are errors. + if (!strictSelectorMatch || + (issueDiagnostic && getLangOptions().ObjCAutoRefCount)) for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) { - // This checks if the methods differ by size & alignment. - if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, true)) - issueWarning = true; + // This checks if the methods differ in type mismatch. + if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, + MMS_loose) && + !isAcceptableMethodMismatch(MethList.Method, Next->Method)) { + issueDiagnostic = true; + if (getLangOptions().ObjCAutoRefCount) + issueError = true; + break; + } } - if (issueWarning) { - if (strictSelectorMatch) + if (issueDiagnostic) { + if (issueError) + Diag(R.getBegin(), diag::err_arc_multiple_method_decl) << Sel << R; + else if (strictSelectorMatch) Diag(R.getBegin(), diag::warn_strict_multiple_method_decl) << Sel << R; else Diag(R.getBegin(), diag::warn_multiple_method_decl) << Sel << R; - Diag(MethList.Method->getLocStart(), diag::note_using) + + Diag(MethList.Method->getLocStart(), + issueError ? diag::note_possibility : diag::note_using) << MethList.Method->getSourceRange(); for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) Diag(Next->Method->getLocStart(), diag::note_also_found) @@ -1796,6 +2151,7 @@ void Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, DefaultSynthesizeProperties(S, IC, IDecl); ImplMethodsVsClassMethods(S, IC, IDecl); AtomicPropertySetterGetterRules(IC, IDecl); + DiagnoseOwningPropertyGetterSynthesis(IC); if (LangOpts.ObjCNonFragileABI2) while (IDecl->getSuperClass()) { @@ -1968,7 +2324,7 @@ Decl *Sema::ActOnMethodDeclaration( } else { ArgType = GetTypeFromParser(ArgInfo[i].Type, &DI); // Perform the default array/function conversions (C99 6.7.5.3p[7,8]). - ArgType = adjustParameterType(ArgType); + ArgType = Context.getAdjustedParameterType(ArgType); } LookupResult R(*this, ArgInfo[i].Name, ArgInfo[i].NameLoc, @@ -2015,7 +2371,7 @@ Decl *Sema::ActOnMethodDeclaration( ArgType = Context.getObjCIdType(); else // Perform the default array/function conversions (C99 6.7.5.3p[7,8]). - ArgType = adjustParameterType(ArgType); + ArgType = Context.getAdjustedParameterType(ArgType); if (ArgType->isObjCObjectType()) { Diag(Param->getLocation(), diag::err_object_cannot_be_passed_returned_by_value) @@ -2104,7 +2460,11 @@ Decl *Sema::ActOnMethodDeclaration( mergeObjCMethodDecls(ObjCMethod, InterfaceMD); } - if (!ObjCMethod->hasRelatedResultType() && + bool ARCError = false; + if (getLangOptions().ObjCAutoRefCount) + ARCError = CheckARCMethodDecl(*this, ObjCMethod); + + if (!ObjCMethod->hasRelatedResultType() && !ARCError && getLangOptions().ObjCInferRelatedResultType) { bool InferRelatedResultType = false; switch (ObjCMethod->getMethodFamily()) { @@ -2114,6 +2474,7 @@ Decl *Sema::ActOnMethodDeclaration( case OMF_mutableCopy: case OMF_release: case OMF_retainCount: + case OMF_performSelector: break; case OMF_alloc: @@ -2255,15 +2616,8 @@ Decl *Sema::ActOnObjCExceptionDecl(Scope *S, Declarator &D) { if (getLangOptions().CPlusPlus) CheckExtraCXXDefaultArguments(D); - TagDecl *OwnedDecl = 0; - TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S, &OwnedDecl); + TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); QualType ExceptionType = TInfo->getType(); - - if (getLangOptions().CPlusPlus && OwnedDecl && OwnedDecl->isDefinition()) { - // Objective-C++: Types shall not be defined in exception types. - Diag(OwnedDecl->getLocation(), diag::err_type_defined_in_param_type) - << Context.getTypeDeclType(OwnedDecl); - } VarDecl *New = BuildObjCExceptionDecl(TInfo, ExceptionType, D.getSourceRange().getBegin(), |