diff options
author | Douglas Gregor <dgregor@apple.com> | 2011-06-11 01:09:30 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2011-06-11 01:09:30 +0000 |
commit | 926df6cfabf3eaa4afc990c097fa4619b76a9b57 (patch) | |
tree | f48a5ca74fa783c76ae30e190d130881fc5febe8 | |
parent | 45937ae10a0f70f74508165aab4f2b63e18ea747 (diff) |
Implement Objective-C Related Result Type semantics.
Related result types apply Cocoa conventions to the type of message
sends and property accesses to Objective-C methods that are known to
always return objects whose type is the same as the type of the
receiving class (or a subclass thereof), such as +alloc and
-init. This tightens up static type safety for Objective-C, so that we
now diagnose mistakes like this:
t.m:4:10: warning: incompatible pointer types initializing 'NSSet *'
with an
expression of type 'NSArray *' [-Wincompatible-pointer-types]
NSSet *array = [[NSArray alloc] init];
^ ~~~~~~~~~~~~~~~~~~~~~~
/System/Library/Frameworks/Foundation.framework/Headers/NSObject.h:72:1:
note:
instance method 'init' is assumed to return an instance of its
receiver
type ('NSArray *')
- (id)init;
^
It also means that we get decent type inference when writing code in
Objective-C++0x:
auto array = [[NSMutableArray alloc] initWithObjects:@"one", @"two",nil];
// ^ now infers NSMutableArray* rather than id
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@132868 91177308-0d34-0410-b5e6-96231b3b80d8
33 files changed, 895 insertions, 68 deletions
diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index bc8ecb1bc9..8f43725d82 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -62,6 +62,11 @@ td { </ul> <li><a href="#checking_type_traits">Checks for Type Traits</a></li> <li><a href="#blocks">Blocks</a></li> +<li><a href="#objc_features">Objective-C Features</a> + <ul> + <li><a href="#objc_instancetype">Related result types</a></li> + </ul> +</li> <li><a href="#overloading-in-c">Function Overloading in C</a></li> <li><a href="#builtins">Builtin Functions</a> <ul> @@ -598,6 +603,73 @@ href="Block-ABI-Apple.txt">Block-ABI-Apple.txt</a>.</p> <p>Query for this feature with __has_extension(blocks).</p> <!-- ======================================================================= --> +<h2 id="objc_features">Objective-C Features</h2> +<!-- ======================================================================= --> + +<h3 id="objc_instancetype">Related result types</h3> + +<p>According to Cocoa conventions, Objective-C methods with certain names ("init", "alloc", etc.) always return objects that are an instance of the receiving class's type. Such methods are said to have a "related result type", meaning that a message send to one of these methods will have the same static type as an instance of the receiver class. For example, given the following classes:</p> + +<blockquote> +<pre> +@interface NSObject ++ (id)alloc; +- (id)init; +@end + +@interface NSArray : NSObject +@end +</pre> +</blockquote> + +<p>and this common initialization pattern</p> + +<blockquote> +<pre> +NSArray *array = [[NSArray alloc] init]; +</pre> +</blockquote> + +<p>the type of the expression <code>[NSArray alloc]</code> is +<code>NSArray*</code> because <code>alloc</code> implicitly has a +related result type. Similarly, the type of the expression +<code>[[NSArray alloc] init]</code> is <code>NSArray*</code>, since +<code>init</code> has a related result type and its receiver is known +to have the type <code>NSArray *</code>. If neither <code>alloc</code> nor <code>init</code> had a related result type, the expressions would have had type <code>id</code>, as declared in the method signature.</p> + +<p>To determine whether a method has a 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 + +<ul> + + <li>the first word is "alloc" or "new", and the method is a class + method, or</li> + + <li>the first word is "autorelease", "init", "retain", or "self", + and the method is an instance method.</li> + +</ul></p> + +<p>If a method with a related result type is overridden by a subclass +method, the subclass method must also return a type that is compatible +with the subclass type. For example:</p> + +<blockquote> +<pre> +@interface NSString : NSObject +- (NSUnrelated *)init; // incorrect usage: NSUnrelated is not NSString or a superclass of NSString +@end +</pre> +</blockquote> + +<p>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.</p> + +<!-- ======================================================================= --> <h2 id="overloading-in-c">Function Overloading in C</h2> <!-- ======================================================================= --> diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 0a4d864cd8..0e698607db 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -135,6 +135,9 @@ private: /// in, inout, etc. unsigned objcDeclQualifier : 6; + /// \brief Indicates whether this method has a related result type. + unsigned RelatedResultType : 1; + // Number of args separated by ':' in a method declaration. unsigned NumSelectorArgs; @@ -171,6 +174,7 @@ private: bool isSynthesized = false, bool isDefined = false, ImplementationControl impControl = None, + bool HasRelatedResultType = false, unsigned numSelectorArgs = 0) : NamedDecl(ObjCMethod, contextDecl, beginLoc, SelInfo), DeclContext(ObjCMethod), Family(InvalidObjCMethodFamily), @@ -178,8 +182,8 @@ private: IsSynthesized(isSynthesized), IsDefined(isDefined), DeclImplementation(impControl), objcDeclQualifier(OBJC_TQ_None), - NumSelectorArgs(numSelectorArgs), MethodDeclType(T), - ResultTInfo(ResultTInfo), + RelatedResultType(HasRelatedResultType), NumSelectorArgs(numSelectorArgs), + MethodDeclType(T), ResultTInfo(ResultTInfo), EndLoc(endLoc), Body(0), SelfDecl(0), CmdDecl(0) {} /// \brief A definition will return its interface declaration. @@ -199,6 +203,7 @@ public: bool isSynthesized = false, bool isDefined = false, ImplementationControl impControl = None, + bool HasRelatedResultType = false, unsigned numSelectorArgs = 0); virtual ObjCMethodDecl *getCanonicalDecl(); @@ -211,6 +216,13 @@ public: } void setObjCDeclQualifier(ObjCDeclQualifier QV) { objcDeclQualifier = QV; } + /// \brief Determine whether this method has a result type that is related + /// to the message receiver's type. + bool hasRelatedResultType() const { return RelatedResultType; } + + /// \brief Note whether this method has a related result type. + void SetRelatedResultType(bool RRT = true) { RelatedResultType = RRT; } + unsigned getNumSelectorArgs() const { return NumSelectorArgs; } void setNumSelectorArgs(unsigned numSelectorArgs) { NumSelectorArgs = numSelectorArgs; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 17356b963d..7890aefc50 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4117,5 +4117,22 @@ def err_invalid_astype_of_different_size : Error< } // end of sema category +let CategoryName = "Related Result Type Issue" in { +// Objective-C related result type compatibility +def warn_related_result_type_compatibility_class : Warning< + "method is expected to return an instance of its class type %0, but " + "is declared to return %1">; +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< + "overridden method is part of the '%select{|alloc|copy|init|mutableCopy|" + "new|autorelease|dealloc|release|retain|retainCount|self}0' method family">; +def note_related_result_type_inferred : Note< + "%select{class|instance}0 method %1 is assumed to return an instance of " + "its receiver type (%2)">; + +} + } // end of sema component. diff --git a/include/clang/Basic/IdentifierTable.h b/include/clang/Basic/IdentifierTable.h index 683ec8312e..b4eca6d8fb 100644 --- a/include/clang/Basic/IdentifierTable.h +++ b/include/clang/Basic/IdentifierTable.h @@ -498,7 +498,8 @@ enum ObjCMethodFamily { OMF_dealloc, OMF_release, OMF_retain, - OMF_retainCount + OMF_retainCount, + OMF_self }; /// Enough bits to store any enumerator in ObjCMethodFamily or diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index a5f6789b7d..f0f1432ca0 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -46,6 +46,8 @@ public: unsigned ObjCNonFragileABI : 1; // Objective-C modern abi enabled unsigned ObjCNonFragileABI2 : 1; // Objective-C enhanced modern abi enabled unsigned ObjCDefaultSynthProperties : 1; // Objective-C auto-synthesized properties. + unsigned ObjCInferRelatedResultType : 1; // Infer Objective-C related return + // types unsigned AppleKext : 1; // Allow apple kext features. unsigned PascalStrings : 1; // Allow Pascal strings @@ -173,6 +175,7 @@ public: GC = ObjC1 = ObjC2 = ObjCNonFragileABI = ObjCNonFragileABI2 = 0; AppleKext = 0; ObjCDefaultSynthProperties = 0; + ObjCInferRelatedResultType = 0; NoConstantCFStrings = 0; InlineVisibilityHidden = 0; C99 = C1X = Microsoft = Borland = CPlusPlus = CPlusPlus0x = 0; CXXOperatorNames = PascalStrings = WritableStrings = ConstStrings = 0; diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 155ecae579..14d8f67b9d 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -493,6 +493,8 @@ def print_ivar_layout : Flag<"-print-ivar-layout">, HelpText<"Enable Objective-C Ivar layout bitmap print trace">; def fobjc_nonfragile_abi : Flag<"-fobjc-nonfragile-abi">, HelpText<"enable objective-c's nonfragile abi">; +def fobjc_infer_related_result_type : Flag<"-fobjc-infer-related-result-type">, + HelpText<"infer Objective-C related result type based on method family">; def ftrapv : Flag<"-ftrapv">, HelpText<"Trap on integer overflow">; def ftrapv_handler : Separate<"-ftrapv-handler">, diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 23b351c78a..72039766d9 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -392,6 +392,10 @@ def fobjc_gc_only : Flag<"-fobjc-gc-only">, Group<f_Group>; def fobjc_gc : Flag<"-fobjc-gc">, Group<f_Group>; def fobjc_legacy_dispatch : Flag<"-fobjc-legacy-dispatch">, Group<f_Group>; def fobjc_new_property : Flag<"-fobjc-new-property">, Group<clang_ignored_f_Group>; +def fobjc_infer_related_result_type : Flag<"-fobjc-infer-related-result-type">, + Group<f_Group>; +def fno_objc_infer_related_result_type : Flag< + "-fno-objc-infer-related-result-type">, Group<f_Group>; // Objective-C ABI options. def fobjc_abi_version_EQ : Joined<"-fobjc-abi-version=">, Group<f_Group>; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index a1c0ba9dcf..bb3ec2dbed 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4981,7 +4981,7 @@ public: SourceLocation EndLoc, // location of the ; or {. tok::TokenKind MethodType, Decl *ClassDecl, ObjCDeclSpec &ReturnQT, ParsedType ReturnType, - Selector Sel, + SourceLocation SelectorStartLoc, Selector Sel, // optional arguments. The number of types/arguments is obtained // from the Sel.getNumArgs(). ObjCArgInfo *ArgInfo, @@ -5079,7 +5079,18 @@ public: SourceLocation RBracLoc, MultiExprArg Args); - + /// \brief Check whether the given new method is a valid override of the + /// given overridden method, and set any properties that should be inherited. + /// + /// \returns True if an error occurred. + bool CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, + const ObjCMethodDecl *Overridden, + bool IsImplementation); + + /// \brief Check whether the given method overrides any methods in its class, + /// calling \c CheckObjCMethodOverride for each overridden method. + bool CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, DeclContext *DC); + enum PragmaOptionsAlignKind { POAK_Native, // #pragma options align=native POAK_Natural, // #pragma options align=natural @@ -5501,11 +5512,24 @@ public: /// \param Method - May be null. /// \param [out] ReturnType - The return type of the send. /// \return true iff there were any incompatible types. - bool CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, Selector Sel, + bool CheckMessageArgumentTypes(QualType ReceiverType, + Expr **Args, unsigned NumArgs, Selector Sel, ObjCMethodDecl *Method, bool isClassMessage, + bool isSuperMessage, SourceLocation lbrac, SourceLocation rbrac, QualType &ReturnType, ExprValueKind &VK); + /// \brief Determine the result of a message send expression based on + /// the type of the receiver, the method expected to receive the message, + /// and the form of the message send. + QualType getMessageSendResultType(QualType ReceiverType, + ObjCMethodDecl *Method, + bool isClassMessage, bool isSuperMessage); + + /// \brief If the given expression involves a message send to a method + /// with a related result type, emit a note describing what happened. + void EmitRelatedResultTypeNote(const Expr *E); + /// CheckBooleanCondition - Diagnose problems involving the use of /// the given expression as a boolean condition (e.g. in an if /// statement). Also performs the standard function and array diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 8904a9561c..fcca95cefd 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -2870,7 +2870,8 @@ Decl *ASTNodeImporter::VisitObjCMethodDecl(ObjCMethodDecl *D) { D->isVariadic(), D->isSynthesized(), D->isDefined(), - D->getImplementationControl()); + D->getImplementationControl(), + D->hasRelatedResultType()); // FIXME: When we decide to merge method definitions, we'll need to // deal with implicit parameters. diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 24d281e8b6..e2c4f38ff9 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -339,12 +339,14 @@ ObjCMethodDecl *ObjCMethodDecl::Create(ASTContext &C, bool isSynthesized, bool isDefined, ImplementationControl impControl, + bool HasRelatedResultType, unsigned numSelectorArgs) { return new (C) ObjCMethodDecl(beginLoc, endLoc, SelInfo, T, ResultTInfo, contextDecl, isInstance, isVariadic, isSynthesized, isDefined, impControl, + HasRelatedResultType, numSelectorArgs); } @@ -446,6 +448,7 @@ ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const { case OMF_release: case OMF_autorelease: case OMF_retainCount: + case OMF_self: if (!isInstanceMethod()) family = OMF_None; break; diff --git a/lib/AST/DumpXML.cpp b/lib/AST/DumpXML.cpp index 12806aad1d..dfe0119f59 100644 --- a/lib/AST/DumpXML.cpp +++ b/lib/AST/DumpXML.cpp @@ -848,6 +848,7 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, setFlag("variadic", D->isVariadic()); setFlag("synthesized", D->isSynthesized()); setFlag("defined", D->isDefined()); + setFlag("related_result_type", D->hasRelatedResultType()); } void visitObjCMethodDeclChildren(ObjCMethodDecl *D) { dispatch(D->getResultType()); diff --git a/lib/Analysis/CocoaConventions.cpp b/lib/Analysis/CocoaConventions.cpp index 4c62f36e6c..946c38c875 100644 --- a/lib/Analysis/CocoaConventions.cpp +++ b/lib/Analysis/CocoaConventions.cpp @@ -44,6 +44,7 @@ cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S) { case OMF_release: case OMF_retain: case OMF_retainCount: + case OMF_self: return NoConvention; case OMF_init: diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index cb1f55b757..4711faa1a5 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/lib/Basic/IdentifierTable.cpp @@ -392,6 +392,7 @@ ObjCMethodFamily Selector::getMethodFamilyImpl(Selector sel) { if (name == "release") return OMF_release; if (name == "retain") return OMF_retain; if (name == "retainCount") return OMF_retainCount; + if (name == "self") return OMF_self; } // The other method families may begin with a prefix of underscores. diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 28e7e42885..fa42cd1f36 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -47,6 +47,23 @@ llvm::Value *CodeGenFunction::EmitObjCProtocolExpr(const ObjCProtocolExpr *E) { return CGM.getObjCRuntime().GenerateProtocolRef(Builder, E->getProtocol()); } +/// \brief Adjust the type of the result of an Objective-C message send +/// expression when the method has a related result type. +static RValue AdjustRelatedResultType(CodeGenFunction &CGF, + const Expr *E, + const ObjCMethodDecl *Method, + RValue Result) { + if (!Method) + return Result; + if (!Method->hasRelatedResultType() || + CGF.getContext().hasSameType(E->getType(), Method->getResultType()) || + !Result.isScalar()) + return Result; + + // We have applied a related result type. Cast the rvalue appropriately. + return RValue::get(CGF.Builder.CreateBitCast(Result.getScalarVal(), + CGF.ConvertType(E->getType()))); +} RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, ReturnValueSlot Return) { @@ -59,15 +76,17 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, bool isClassMessage = false; ObjCInterfaceDecl *OID = 0; // Find the receiver + QualType ReceiverType; llvm::Value *Receiver = 0; switch (E->getReceiverKind()) { case ObjCMessageExpr::Instance: Receiver = EmitScalarExpr(E->getInstanceReceiver()); + ReceiverType = E->getInstanceReceiver()->getType(); break; case ObjCMessageExpr::Class: { - const ObjCObjectType *ObjTy - = E->getClassReceiver()->getAs<ObjCObjectType>(); + ReceiverType = E->getClassReceiver(); + const ObjCObjectType *ObjTy = ReceiverType->getAs<ObjCObjectType>(); assert(ObjTy && "Invalid Objective-C class message send"); OID = ObjTy->getInterface(); assert(OID && "Invalid Objective-C class message send"); @@ -77,11 +96,13 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, } case ObjCMessageExpr::SuperInstance: + ReceiverType = E->getSuperType(); Receiver = LoadObjCSelf(); isSuperMessage = true; break; case ObjCMessageExpr::SuperClass: + ReceiverType = E->getSuperType(); Receiver = LoadObjCSelf(); isSuperMessage = true; isClassMessage = true; @@ -94,24 +115,27 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, QualType ResultType = E->getMethodDecl() ? E->getMethodDecl()->getResultType() : E->getType(); + RValue result; if (isSuperMessage) { // super is only valid in an Objective-C method const ObjCMethodDecl *OMD = cast<ObjCMethodDecl>(CurFuncDecl); bool isCategoryImpl = isa<ObjCCategoryImplDecl>(OMD->getDeclContext()); - return Runtime.GenerateMessageSendSuper(*this, Return, ResultType, - E->getSelector(), - OMD->getClassInterface(), - isCategoryImpl, - Receiver, - isClassMessage, - Args, - E->getMethodDecl()); + result = Runtime.GenerateMessageSendSuper(*this, Return, ResultType, + E->getSelector(), + OMD->getClassInterface(), + isCategoryImpl, + Receiver, + isClassMessage, + Args, + E->getMethodDecl()); + } else { + result = Runtime.GenerateMessageSend(*this, Return, ResultType, + E->getSelector(), + Receiver, Args, OID, + E->getMethodDecl()); } - - return Runtime.GenerateMessageSend(*this, Return, ResultType, - E->getSelector(), - Receiver, Args, OID, - E->getMethodDecl()); + + return AdjustRelatedResultType(*this, E, E->getMethodDecl(), result); } /// StartObjCMethod - Begin emission of an ObjCMethod. This generates @@ -711,26 +735,31 @@ RValue CodeGenFunction::EmitLoadOfPropertyRefLValue(LValue LV, const ObjCPropertyRefExpr *E = LV.getPropertyRefExpr(); QualType ResultType = E->getGetterResultType(); Selector S; + const ObjCMethodDecl *method; if (E->isExplicitProperty()) { const ObjCPropertyDecl *Property = E->getExplicitProperty(); S = Property->getGetterName(); + method = Property->getGetterMethodDecl(); } else { - const ObjCMethodDecl *Getter = E->getImplicitPropertyGetter(); - S = Getter->getSelector(); + method = E->getImplicitPropertyGetter(); + S = method->getSelector(); } llvm::Value *Receiver = LV.getPropertyRefBaseAddr(); // Accesses to 'super' follow a different code path. if (E->isSuperReceiver()) - return GenerateMessageSendSuper(*this, Return, ResultType, - S, Receiver, CallArgList()); - + return AdjustRelatedResultType(*this, E, method, + GenerateMessageSendSuper(*this, Return, + ResultType, + S, Receiver, + CallArgList())); const ObjCInterfaceDecl *ReceiverClass = (E->isClassReceiver() ? E->getClassReceiver() : 0); - return CGM.getObjCRuntime(). - GenerateMessageSend(*this, Return, ResultType, S, - Receiver, CallArgList(), ReceiverClass); + return AdjustRelatedResultType(*this, E, method, + CGM.getObjCRuntime(). + GenerateMessageSend(*this, Return, ResultType, S, + Receiver, CallArgList(), ReceiverClass)); } void CodeGenFunction::EmitStoreThroughPropertyRefLValue(RValue Src, diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index bc3ff6873b..8cd7adc42e 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -1542,6 +1542,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_lax_vector_conversions)) CmdArgs.push_back("-fno-lax-vector-conversions"); + // -fobjc-infer-related-result-type is the default. + if (Args.hasFlag(options::OPT_fobjc_infer_related_result_type, + options::OPT_fno_objc_infer_related_result_type, + /*Default=*/true)) + CmdArgs.push_back("-fobjc-infer-related-result-type"); + // Handle -fobjc-gc and -fobjc-gc-only. They are exclusive, and -fobjc-gc-only // takes precedence. const Arg *GCArg = Args.getLastArg(options::OPT_fobjc_gc_only); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index b8b5011bbf..536512a121 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -670,6 +670,9 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-fobjc-gc-only"); } } + if (Opts.ObjCInferRelatedResultType) + Res.push_back("-fobjc-infer-related-result-type"); + if (Opts.AppleKext) Res.push_back("-fapple-kext"); @@ -1485,6 +1488,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, else if (Args.hasArg(OPT_fobjc_gc)) Opts.setGCMode(LangOptions::HybridGC); + if (Args.hasArg(OPT_fobjc_infer_related_result_type)) + Opts.ObjCInferRelatedResultType = 1; + if (Args.hasArg(OPT_fapple_kext)) { if (!Opts.CPlusPlus) Diags.Report(diag::warn_c_kext); diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index fdbedc54d1..a8c18c01a2 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -874,8 +874,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, Selector Sel = PP.getSelectorTable().getNullarySelector(SelIdent); Decl *Result = Actions.ActOnMethodDeclaration(getCurScope(), mLoc, Tok.getLocation(), - mType, IDecl, DSRet, ReturnType, Sel, - 0, + mType, IDecl, DSRet, ReturnType, + selLoc, Sel, 0, CParamInfo.data(), CParamInfo.size(), methodAttrs.getList(), MethodImplKind, false, MethodDefinition); @@ -1000,8 +1000,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, &KeyIdents[0]); Decl *Result = Actions.ActOnMethodDeclaration(getCurScope(), mLoc, Tok.getLocation(), - mType, IDecl, DSRet, ReturnType, Sel, - &ArgInfos[0], + mType, IDecl, DSRet, ReturnType, + selLoc, Sel, &ArgInfos[0], CParamInfo.data(), CParamInfo.size(), methodAttrs.getList(), MethodImplKind, isVariadic, MethodDefinition); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 9967fd3ebf..c2fee32aea 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1936,7 +1936,9 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, for (ObjCMethodDecl::param_iterator oi = oldMethod->param_begin(), ni = newMethod->param_begin(), ne = newMethod->param_end(); ni != ne; ++ni, ++oi) - mergeParamDeclAttributes(*ni, *oi, Context); + mergeParamDeclAttributes(*ni, *oi, Context); + + CheckObjCMethodOverride(newMethod, oldMethod, true); } /// MergeVarDeclTypes - We parsed a variable 'New' which has the same name and diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 4e41aa93e5..de9097e98b 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -24,6 +24,141 @@ using namespace clang; +bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, + const ObjCMethodDecl *Overridden, + bool IsImplementation) { + if (Overridden->hasRelatedResultType() && + !NewMethod->hasRelatedResultType()) { + // This can only happen when the method follows a naming convention that + // implies a related result type, and the original (overridden) method has + // a suitable return type, but the new (overriding) method does not have + // a suitable return type. + QualType ResultType = NewMethod->getResultType(); + SourceRange ResultTypeRange; + if (const TypeSourceInfo *ResultTypeInfo + = NewMethod->getResultTypeSourceInfo()) + ResultTypeRange = ResultTypeInfo->getTypeLoc().getSourceRange(); + + // Figure out which class this method is part of, if any. + ObjCInterfaceDecl *CurrentClass + = dyn_cast<ObjCInterfaceDecl>(NewMethod->getDeclContext()); + if (!CurrentClass) { + DeclContext *DC = NewMethod->getDeclContext(); + if (ObjCCategoryDecl *Cat = dyn_cast<ObjCCategoryDecl>(DC)) + CurrentClass = Cat->getClassInterface(); + else if (ObjCImplDecl *Impl = dyn_cast<ObjCImplDecl>(DC)) + CurrentClass = Impl->getClassInterface(); + else if (ObjCCategoryImplDecl *CatImpl + = dyn_cast<ObjCCategoryImplDecl>(DC)) + CurrentClass = CatImpl->getClassInterface(); + } + + if (CurrentClass) { + Diag(NewMethod->getLocation(), + diag::warn_related_result_type_compatibility_class) + << Context.getObjCInterfaceType(CurrentClass) + << ResultType + << ResultTypeRange; + } else { + Diag(NewMethod->getLocation(), + diag::warn_related_result_type_compatibility_protocol) + << ResultType + << ResultTypeRange; + } + + Diag(Overridden->getLocation(), diag::note_related_result_type_overridden) + << Overridden->getMethodFamily(); + } + + return false; +} + + +static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, + DeclContext *DC, + bool SkipCurrent = true) { + if (!DC) + return false; + + if (!SkipCurrent) { + // Look for this method. If we find it, we're done. + Selector Sel = NewMethod->getSelector(); + bool IsInstance = NewMethod->isInstanceMethod(); + DeclContext::lookup_const_iterator Meth, MethEnd; + for (llvm::tie(Meth, MethEnd) = DC->lookup(Sel); Meth != MethEnd; ++Meth) { + ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(*Meth); + if (MD && MD->isInstanceMethod() == IsInstance) + return S.CheckObjCMethodOverride(NewMethod, MD, false); + } + } + + if (ObjCInterfaceDecl *Class = llvm::dyn_cast<ObjCInterfaceDecl>(DC)) { + // Look through categories. + for (ObjCCategoryDecl *Category = Class->getCategoryList(); + Category; Category = Category->getNextClassCategory()) { + if (CheckObjCMethodOverrides(S, NewMethod, Category, false)) + return true; + } + + // Look through protocols. + for (ObjCList<ObjCProtocolDecl>::iterator I = Class->protocol_begin(), + IEnd = Class->protocol_end(); + I != IEnd; ++I) + if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) + return true; + + // Look in our superclass. + return CheckObjCMethodOverrides(S, NewMethod, Class->getSuperClass(), + false); + } + + if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(DC)) { + // Look through protocols. + for (ObjCList<ObjCProtocolDecl>::iterator I = Category->protocol_begin(), + IEnd = Category->protocol_end(); + I != IEnd; ++I) + if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) + return true; + + return false; + } + + if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(DC)) { + // Look through protocols. + for (ObjCList<ObjCProtocolDecl>::iterator I = Protocol->protocol_begin(), + IEnd = Protocol->protocol_end(); + I != IEnd; ++I) + if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) + return true; + + return false; + } + + return false; +} + +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()); + + if (ObjCCategoryImplDecl *CatImpl = dyn_cast<ObjCCategoryImplDecl>(DC)) + return ::CheckObjCMethodOverrides(*this, NewMethod, + CatImpl->getClassInterface()); + + return ::CheckObjCMethodOverrides(*this, NewMethod, CurContext); +} + static void DiagnoseObjCImplementedDeprecations(Sema &S, NamedDecl *ND, SourceLocation ImplLoc, @@ -1717,11 +1852,71 @@ bool containsInvalidMethodImplAttribute(const AttrVec &A) { return false; } +/// \brief Check whether the declared result type of the given Objective-C +/// method declaration is compatible with the method's class. +/// +static bool +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 + // declared result type is compatible if: + if (const ObjCObjectPointerType *ResultObjectType + = ResultType->getAs<ObjCObjectPointerType>()) { + // - it is id or qualified id, or + if (ResultObjectType->isObjCIdType() || + ResultObjectType->isObjCQualifiedIdType()) + return false; + + if (CurrentClass) { + if (ObjCInterfaceDecl *ResultClass + = ResultObjectType->getInterfaceDecl()) { + // - it is the same as the method's class type, or + if (CurrentClass == ResultClass) + return false; + + // - it is a superclass of the method's class type + if (ResultClass->isSuperClassOf(CurrentClass)) + return false; + } + } + } + + return true; +} + +/// \brief Determine if any method in the global method pool has an inferred +/// result type. +static bool +anyMethodInfersRelatedResultType(Sema &S, Selector Sel, bool IsInstance) { + Sema::GlobalMethodPool::iterator Pos = S.MethodPool.find(Sel); + if (Pos == S.MethodPool.end()) { + if (S.ExternalSource) + Pos = S.ReadMethodPool(Sel); + else + return 0; + } + + ObjCMethodList &List = IsInstance ? Pos->second.first : Pos->second.second; + for (ObjCMethodList *M = &List; M; M = M->Next) { + if (M->Method && M->Method->hasRelatedResultType()) + return true; + } + + return false; +} + Decl *Sema::ActOnMethodDeclaration( Scope *S, SourceLocation MethodLoc, SourceLocation EndLoc, tok::TokenKind MethodType, Decl *ClassDecl, ObjCDeclSpec &ReturnQT, ParsedType ReturnType, + SourceLocation SelectorStartLoc, Selector Sel, // optional arguments. The number of types/arguments is obtained // from the Sel.getNumArgs(). @@ -1746,7 +1941,7 @@ Decl *Sema::ActOnMethodDeclaration( Diag(MethodLoc, diag::err_object_cannot_be_passed_returned_by_value) << 0 << resultDeclType; return 0; - } + } } else // get the type for "id". resultDeclType = Context.getObjCIdType(); @@ -1756,9 +1951,10 @@ Decl *Sema::ActOnMethodDeclaration( cast<DeclContext>(ClassDecl), MethodType == tok::minus, isVariadic, false, false, - MethodDeclKind == tok::objc_optional ? - ObjCMethodDecl::Optional : - ObjCMethodDecl::Required); + MethodDeclKind == tok::objc_optional + ? ObjCMethodDecl::Optional + : ObjCMethodDecl::Required, + false); llvm::SmallVector<ParmVarDecl*, 16> Params; @@ -1854,6 +2050,7 @@ Decl *Sema::ActOnMethodDeclaration( } InterfaceMD = ImpDecl->getClassInterface()->getMethod(Sel, MethodType == tok::minus); + if (ObjCMethod->hasAttrs() && containsInvalidMethodImplAttribute(ObjCMethod->getAttrs())) Diag(EndLoc, diag::warn_attribute_method_def); @@ -1866,6 +2063,10 @@ Decl *Sema::ActOnMethodDeclaration( PrevMethod = CatImpDecl->getClassMethod(Sel); CatImpDecl->addClassMethod(ObjCMethod); } + + if (ObjCCategoryDecl *Cat = CatImpDecl->getCategoryDecl()) + InterfaceMD = Cat->getMethod(Sel, MethodType == tok::minus); + if (ObjCMethod->hasAttrs() && containsInvalidMethodImplAttribute(ObjCMethod->getAttrs())) Diag(EndLoc, diag::warn_attribute_method_def); @@ -1879,10 +2080,65 @@ Decl *Sema::ActOnMethodDeclaration( Diag(PrevMethod->getLocation(), diag::note_previous_declaration); } + // If this Objective-C method does not have a related result type, but we + // are allowed to infer related result types, try to do so based on the + // method family. + ObjCInterfaceDecl *CurrentClass = dyn_cast<ObjCInterfaceDecl>(ClassDecl); + if (!CurrentClass) { + if (ObjCCategoryDecl *Cat = dyn_cast<ObjCCategoryDecl>(ClassDecl)) + CurrentClass = Cat->getClassInterface(); + else if (ObjCImplDecl *Impl = dyn_cast<ObjCImplDecl>(ClassDecl)) + CurrentClass = Impl->getClassInterface(); + else if (ObjCCategoryImplDecl *CatImpl + = dyn_cast<ObjCCategoryImplDecl>(ClassDecl)) + CurrentClass = CatImpl->getClassInterface(); + } + // Merge information down from the interface declaration if we have one. - if (InterfaceMD) + if (InterfaceMD) { + // Inherit the related result type, if we can. + if (InterfaceMD->hasRelatedResultType() && + !CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass)) + ObjCMethod->SetRelatedResultType(); + mergeObjCMethodDecls(ObjCMethod, InterfaceMD); - + } + + if (!ObjCMethod->hasRelatedResultType() && + getLangOptions().ObjCInferRelatedResultType) { + bool InferRelatedResultType = false; + switch (ObjCMethod->getMethodFamily()) { + case OMF_None: + case OMF_copy: + case OMF_dealloc: + case OMF_mutableCopy: + case OMF_release: + case OMF_retainCount: + break; + + case OMF_alloc: + case OMF_new: + InferRelatedResultType = ObjCMethod->isClassMethod(); + break; + + case OMF_init: + case OMF_autorelease: + case OMF_retain: + case OMF_self: + InferRelatedResultType = ObjCMethod->isInstanceMethod(); + break; + } + + if (InferRelatedResultType && + !CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass)) + ObjCMethod->SetRelatedResultType(); + + if (!InterfaceMD && + anyMethodInfersRelatedResultType(*this, ObjCMethod->getSelector(), + ObjCMethod->isInstanceMethod())) + CheckObjCMethodOverrides(ObjCMethod, cast<DeclContext>(ClassDecl)); + } + return ObjCMethod; } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index bb6a414f44..dafd56bf34 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -4328,7 +4328,11 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); - return Owned(new (Context) ObjCPropertyRefExpr(PD, PD->getType(), + QualType T = PD->getType(); + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + T = getMessageSendResultType(BaseType, Getter, false, false); + + return Owned(new (Context) ObjCPropertyRefExpr(PD, T, VK_LValue, OK_ObjCProperty, MemberLoc, @@ -4346,7 +4350,8 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, if (Decl *SDecl = FindGetterSetterNameDecl(OPT, /*Property id*/0, SetterSel, Context)) SMD = dyn_cast<ObjCMethodDecl>(SDecl); - QualType PType = OMD->getSendResultType(); + QualType PType = getMessageSendResultType(BaseType, OMD, false, + false); ExprValueKind VK = VK_LValue; if (!getLangOptions().CPlusPlus && @@ -4414,7 +4419,8 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, ExprValueKind VK = VK_LValue; if (Getter) { - PType = Getter->getSendResultType(); + PType = getMessageSendResultType(QualType(OT, 0), Getter, true, + false); if (!getLangOptions().CPlusPlus && IsCForbiddenLValueType(Context, PType)) VK = VK_RValue; @@ -8345,20 +8351,31 @@ ExprResult Sema::ConvertPropertyForRValue(Expr *E) { E->getObjectKind() == OK_ObjCProperty); const ObjCPropertyRefExpr *PRE = E->getObjCProperty(); + QualType T = E->getType(); + QualType ReceiverType; + if (PRE->isObjectReceiver()) + ReceiverType = PRE->getBase()->getType(); + else if (PRE->isSuperReceiver()) + ReceiverType = PRE->getSuperReceiverType(); + else + ReceiverType = Context.getObjCInterfaceType(PRE->getClassReceiver()); + ExprValueKind VK = VK_RValue; if (PRE->isImplicitProperty()) { - if (const ObjCMethodDecl *GetterMethod = + if (ObjCMethodDecl *GetterMethod = PRE->getImplicitPropertyGetter()) { - QualType Result = GetterMethod->getResultType(); - VK = Expr::getValueKindForType(Result); + T = getMessageSendResultType(ReceiverType, GetterMethod, + PRE->isClassReceiver(), + PRE->isSuperReceiver()); + VK = Expr::getValueKindForType(GetterMethod->getResultType()); } else { Diag(PRE->getLocation(), diag::err_getter_not_found) << PRE->getBase()->getType(); } } - - E = ImplicitCastExpr::Create(Context, E->getType(), CK_GetObjCProperty, + + E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty, E, 0, VK); ExprResult Result = MaybeBindToTemporary(E); @@ -9851,6 +9868,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, *Complained = false; // Decode the result (notice that AST's are still created for extensions). + bool CheckInferredResultType = false; bool isInvalid = false; unsigned DiagKind; FixItHint Hint; @@ -9867,6 +9885,8 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, case IncompatiblePointer: MakeObjCStringLiteralFixItHint(*this, DstType, SrcExpr, Hint); DiagKind = diag::ext_typecheck_convert_incompatible_pointer; + CheckInferredResultType = DstType->isObjCObjectPointerType() && + SrcType->isObjCObjectPointerType(); break; case IncompatiblePointerSign: DiagKind = diag::ext_typecheck_convert_incompatible_pointer_sign; @@ -9948,6 +9968,9 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, Diag(Loc, DiagKind) << FirstType << SecondType << Action << SrcExpr->getSourceRange() << Hint; + if (CheckInferredResultType) + EmitRelatedResultTypeNote(SrcExpr); + if (Complained) *Complained = true; return isInvalid; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 2cd495bf14..27659f6507 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -2221,6 +2221,10 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, diag::ext_typecheck_convert_incompatible_pointer) << From->getType() << ToType << Action << From->getSourceRange(); + + if (From->getType()->isObjCObjectPointerType() && + ToType->isObjCObjectPointerType()) + EmitRelatedResultTypeNote(From); } CastKind Kind = CK_Invalid; diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 3444cb5a49..cb5c1e0d0c 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -241,10 +241,72 @@ ObjCMethodDecl *Sema::tryCaptureObjCSelf() { return method; } +QualType Sema::getMessageSendResultType(QualType ReceiverType, + ObjCMethodDecl *Method, + bool isClassMessage, bool isSuperMessage) { + assert(Method && "Must have a method"); + if (!Method->hasRelatedResultType()) + return Method->getSendResultType(); + + // If a method has a related return type: + // - if the method found is an instance method, but the message send + // was a class message send, T is the declared return type of the method + // found + if (Method->isInstanceMethod() && isClassMessage) + return Method->getSendResultType(); + + // - if the receiver is super, T is a pointer to the class of the + // enclosing method definition + if (isSuperMessage) { + if (ObjCMethodDecl *CurMethod = getCurMethodDecl()) + if (ObjCInterfaceDecl *Class = CurMethod->getClassInterface()) + return Context.getObjCObjectPointerType( + Context.getObjCInterfaceType(Class)); + } + + // - if the receiver is the name of a class U, T is a pointer to U + if (ReceiverType->getAs<ObjCInterfaceType>() || + ReceiverType->isObjCQualifiedInterfaceType()) + return Context.getObjCObjectPointerType(ReceiverType); + // - if the receiver is of type Class or qualified Class type, + // T is the declared return type of the method. + if (ReceiverType->isObjCClassType() || + ReceiverType->isObjCQualifiedClassType()) + return Method->getSendResultType(); + + // - if the receiver is id, qualified id, Class, or qualified Class, T + // is the receiver type, otherwise + // - T is the type of the receiver expression. + return ReceiverType; +} + +void Sema::EmitRelatedResultTypeNote(const Expr *E) { + E = E->IgnoreParenImpCasts(); + const ObjCMessageExpr *MsgSend = dyn_cast<ObjCMessageExpr>(E); + if (!MsgSend) + return; + + const ObjCMethodDecl *Method = MsgSend->getMethodDecl(); + if (!Method) + return; + + if (!Method->hasRelatedResultType()) + return; + + if (Context.hasSameUnqualifiedType(Method->getResultType() + .getNonReferenceType(), + MsgSend->getType())) + return; + + Diag(Method->getLocation(), diag::note_related_result_type_inferred) + << Method->isInstanceMethod() << Method->getSelector() + << MsgSend->getType(); +} -bool Sema::CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, +bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, + Expr **Args, unsigned NumArgs, Selector Sel, ObjCMethodDecl *Method, - bool isClassMessage, + bool isClassMessage, bool isSuperMessage, SourceLocation lbrac, SourceLocation rbrac, QualType &ReturnType, ExprValueKind &VK) { if (!Method) { @@ -268,7 +330,8 @@ bool Sema::CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, return false; } - ReturnType = Method->getSendResultType(); + ReturnType = getMessageSendResultType(ReceiverType, Method, isClassMessage, + isSuperMessage); VK = Expr::getValueKindForType(Method->getResultType()); unsigned NumNamedArgs = Sel.getNumArgs(); @@ -456,9 +519,12 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, ResTy = ResTy.getNonLValueExprType(Context); Selector Sel = PP.getSelectorTable().getNullarySelector(Member); ObjCMethodDecl *Getter = IFace->lookupInstanceMethod(Sel); - if (DiagnosePropertyAccessorMismatch(PD, Getter, MemberLoc)) - ResTy = Getter->getResultType(); - + if (Getter && + (Getter->hasRelatedResultType() + || DiagnosePropertyAccessorMismatch(PD, Getter, MemberLoc))) + ResTy = getMessageSendResultType(QualType(OPT, 0), Getter, false, + Super); + if (Super) return Owned(new (Context) ObjCPropertyRefExpr(PD, ResTy, VK_LValue, OK_ObjCProperty, @@ -476,14 +542,18 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); + + QualType T = PD->getType(); + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + T = getMessageSendResultType(QualType(OPT, 0), Getter, false, Super); if (Super) - return Owned(new (Context) ObjCPropertyRefExpr(PD, PD->getType(), + return Owned(new (Context) ObjCPropertyRefExpr(PD, T, VK_LValue, OK_ObjCProperty, MemberLoc, SuperLoc, SuperType)); else - return Owned(new (Context) ObjCPropertyRefExpr(PD, PD->getType(), + return Owned(new (Context) ObjCPropertyRefExpr(PD, T, VK_LValue, OK_ObjCProperty, MemberLoc, @@ -540,7 +610,7 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, if (Getter || Setter) { QualType PType; if (Getter) - PType = Getter->getSendResultType(); + PType = getMessageSendResultType(QualType(OPT, 0), Getter, false, Super); else { ParmVarDecl *ArgDecl = *Setter->param_begin(); PType = ArgDecl->getType(); @@ -614,10 +684,14 @@ ActOnClassPropertyRefExpr(IdentifierInfo &receiverName, IdentifierInfo *receiverNamePtr = &receiverName; ObjCInterfaceDecl *IFace = getObjCInterfaceDecl(receiverNamePtr, receiverNameLoc); + + bool IsSuper = false; if (IFace == 0) { // If the "receiver" is 'super' in a method, handle it as an expression-like // property reference. if (receiverNamePtr->isStr("super")) { + IsSuper = true; + if (ObjCMethodDecl *CurMethod = tryCaptureObjCSelf()) { if (CurMethod->isInstanceMethod()) { QualType T = @@ -686,7 +760,9 @@ ActOnClassPropertyRefExpr(IdentifierInfo &receiverName, ExprValueKind VK = VK_LValue; if (Getter) { - PType = Getter->getSendResultType(); + PType = getMessageSendResultType(Context.getObjCInterfaceType(IFace), + Getter, true, + receiverNamePtr->isStr("super")); if (!getLangOptions().CPlusPlus && !PType.hasQualifiers() && PType->isVoidType()) VK = VK_RValue; @@ -699,6 +775,13 @@ ActOnClassPropertyRefExpr(IdentifierInfo &receiverName, ExprObjectKind OK = (VK == VK_RValue ? OK_Ordinary : OK_ObjCProperty); + if (IsSuper) + return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, + PType, VK, OK, + propertyNameLoc, + receiverNameLoc, + Context.getObjCInterfaceType(IFace))); + return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, PType, VK, OK, propertyNameLoc, @@ -955,8 +1038,9 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, unsigned NumArgs = ArgsIn.size(); Expr **Args = reinterpret_cast<Expr **>(ArgsIn.release()); - if (CheckMessageArgumentTypes(Args, NumArgs, Sel, Method, true, - LBracLoc, RBracLoc, ReturnType, VK)) + if (CheckMessageArgumentTypes(ReceiverType, Args, NumArgs, Sel, Method, true, + SuperLoc.isValid(), LBracLoc, RBracLoc, + ReturnType, VK)) return ExprError(); if (Method && !Method->getResultType()->isVoidType() && @@ -1238,7 +1322,8 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, ExprValueKind VK = VK_RValue; bool ClassMessage = (ReceiverType->isObjCClassType() || ReceiverType->isObjCQualifiedClassType()); - if (CheckMessageArgumentTypes(Args, NumArgs, Sel, Method, ClassMessage, + if (CheckMessageArgumentTypes(ReceiverType, Args, NumArgs, Sel, Method, + ClassMessage, SuperLoc.isValid(), LBracLoc, RBracLoc, ReturnType, VK)) return ExprError(); diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 11061d53f1..a33f5d0b2f 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -4322,6 +4322,9 @@ bool InitializationSequence::Diagnose(Sema &S, << Args[0]->isLValue() << Args[0]->getType() << Args[0]->getSourceRange(); + if (DestType.getNonReferenceType()->isObjCObjectPointerType() && + Args[0]->getType()->isObjCObjectPointerType()) + S.EmitRelatedResultTypeNote(Args[0]); break; case FK_ConversionFailed: { @@ -4332,6 +4335,9 @@ bool InitializationSequence::Diagnose(Sema &S, << Args[0]->isLValue() << FromType << Args[0]->getSourceRange(); + if (DestType.getNonReferenceType()->isObjCObjectPointerType() && + Args[0]->getType()->isObjCObjectPointerType()) + S.EmitRelatedResultTypeNote(Args[0]); break; } diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index d4fc980e22..d7c0a543ee 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -1682,15 +1682,26 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { return ActOnBlockReturnStmt(ReturnLoc, RetValExp); QualType FnRetType; + QualType DeclaredRetType; if (const FunctionDecl *FD = getCurFunctionDecl()) { FnRetType = FD->getResultType(); + DeclaredRetType = FnRetType; if (FD->hasAttr<NoReturnAttr>() || FD->getType()->getAs<FunctionType>()->getNoReturnAttr()) Diag(ReturnLoc, diag::warn_noreturn_function_has_return_expr) << getCurFunctionOrMethodDecl()->getDeclName(); - } else if (ObjCMethodDecl *MD = getCurMethodDecl()) - FnRetType = MD->getResultType(); - else // If we don't have a function/method context, bail. + } else if (ObjCMethodDecl *MD = getCurMethodDecl()) { + DeclaredRetType = MD->getResultType(); + if (MD->hasRelatedResultType() && MD->getClassInterface()) { + // In the implementation of a method with a related return type, the + // type used to type-check the validity of return statements within the + // method body is a pointer to the type of the class being implemented. + FnRetType = Context.getObjCInterfaceType(MD->getClassInterface()); + FnRetType = Context.getObjCObjectPointerType(FnRetType); + } else { + FnRetType = DeclaredRetType; + } + } else // If we don't have a function/method context, bail. return StmtError(); ReturnStmt *Result = 0; @@ -1764,6 +1775,17 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { } if (RetValExp) { + // If we type-checked an Objective-C method's return type based + // on a related return type, we may need to adjust the return + // type again. Do so now. + if (DeclaredRetType != FnRetType) { + ExprResult result = PerformImplicitConversion(RetValExp, + DeclaredRetType, + AA_Returning); + if (result.isInvalid()) return StmtError(); + RetValExp = result.take(); + } + CheckImplicitConversions(RetValExp, ReturnLoc); RetValExp = MaybeCreateExprWithCleanups(RetValExp); } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 3c3993f412..8fb20d22b7 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -92,6 +92,7 @@ PCHValidator::ReadLanguageOptions(const LangOptions &LangOpts) { PARSE_LANGOPT_IMPORTANT(AppleKext, diag::warn_pch_apple_kext); PARSE_LANGOPT_IMPORTANT(ObjCDefaultSynthProperties, diag::warn_pch_objc_auto_properties); + PARSE_LANGOPT_BENIGN(ObjCInferRelatedResultType) PARSE_LANGOPT_IMPORTANT(NoConstantCFStrings, diag::warn_pch_no_constant_cfstrings); PARSE_LANGOPT_BENIGN(PascalStrings); @@ -2934,6 +2935,7 @@ bool ASTReader::ParseLanguageOptions( PARSE_LANGOPT(ObjCNonFragileABI2); PARSE_LANGOPT(AppleKext); PARSE_LANGOPT(ObjCDefaultSynthProperties); + PARSE_LANGOPT(ObjCInferRelatedResultType); PARSE_LANGOPT(NoConstantCFStrings); PARSE_LANGOPT(PascalStrings); PARSE_LANGOPT(WritableStrings); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index d2b014842c..6e9d4d4820 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -468,6 +468,7 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { MD->setDefined(Record[Idx++]); MD->setDeclImplementation((ObjCMethodDecl::ImplementationControl)Record[Idx++]); MD->setObjCDeclQualifier((Decl::ObjCDeclQualifier)Record[Idx++]); + MD->SetRelatedResultType(Record[Idx++]); MD->setNumSelectorArgs(unsigned(Record[Idx++])); MD->setResultType(Reader.GetType(Record[Idx++])); MD->setResultTypeSourceInfo(GetTypeSourceInfo(Record, Idx)); diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 194d6c31cf..ba9032e0d3 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1050,6 +1050,7 @@ void ASTWriter::WriteLanguageOptions(const LangOptions &LangOpts) { Record.push_back(LangOpts.AppleKext); // Apple's kernel extensions ABI Record.push_back(LangOpts.ObjCDefaultSynthProperties); // Objective-C auto-synthesized // properties enabled. + Record.push_back(LangOpts.ObjCInferRelatedResultType); Record.push_back(LangOpts.NoConstantCFStrings); // non cfstring generation enabled.. Record.push_back(LangOpts.PascalStrings); // Allow Pascal strings diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 71eb5e8231..c6b159c50b 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -404,6 +404,7 @@ void ASTDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) { Record.push_back(D->getImplementationControl()); // FIXME: stable encoding for in/out/inout/bycopy/byref/oneway Record.push_back(D->getObjCDeclQualifier()); + Record.push_back(D->hasRelatedResultType()); Record.push_back(D->getNumSelectorArgs()); Writer.AddTypeRef(D->getResultType(), Record); Writer.AddTypeSourceInfo(D->getResultTypeSourceInfo(), Record); diff --git a/test/Driver/rewrite-objc.m b/test/Driver/rewrite-objc.m index e3d2c0f46d..2fe2ccec6a 100644 --- a/test/Driver/rewrite-objc.m +++ b/test/Driver/rewrite-objc.m @@ -3,7 +3,7 @@ // TEST0: clang{{.*}}" "-cc1" // TEST0: "-rewrite-objc" // FIXME: CHECK-NOT is broken somehow, it doesn't work here. Check adjacency instead. -// TEST0: "-fmessage-length" "0" "-fobjc-exceptions" "-fdiagnostics-show-option" +// TEST0: "-fmessage-length" "0" "-fobjc-infer-related-result-type" "-fobjc-exceptions" "-fdiagnostics-show-option" // TEST0: rewrite-objc.m" // RUN: not %clang -ccc-no-clang -ccc-host-triple unknown -rewrite-objc %s -o - -### 2>&1 | \ diff --git a/test/PCH/objc_methods.h b/test/PCH/objc_methods.h index 4c6b1e1cce..bd77535434 100644 --- a/test/PCH/objc_methods.h +++ b/test/PCH/objc_methods.h @@ -2,7 +2,7 @@ @interface TestPCH + alloc; -- (void)instMethod; +- (id)init; @end @class TestForwardClassDecl; diff --git a/test/PCH/objc_methods.m b/test/PCH/objc_methods.m index e90a463dce..3311813c98 100644 --- a/test/PCH/objc_methods.m +++ b/test/PCH/objc_methods.m @@ -12,5 +12,5 @@ void func() { // AliasForTestPCH *zz; xx = [TestPCH alloc]; - [xx instMethod]; + [xx init]; } diff --git a/test/SemaObjC/related-result-type-inference.m b/test/SemaObjC/related-result-type-inference.m new file mode 100644 index 0000000000..f5399180c4 --- /dev/null +++ b/test/SemaObjC/related-result-type-inference.m @@ -0,0 +1,171 @@ +// RUN: %clang_cc1 -fobjc-infer-related-result-type -verify %s + +@interface Unrelated +@end + +@interface NSObject ++ (id)new; ++ (id)alloc; +- (NSObject *)init; + +- (id)retain; // expected-note{{instance method 'retain' is assumed to return an instance of its receiver type ('NSArray *')}} +- autorelease; + +- (id)self; + +- (id)copy; +- (id)mutableCopy; + +// Do not infer when instance/class mismatches +- (id)newNotInferred; +- (id)alloc; ++ (id)initWithBlarg; ++ (id)self; + +// Do not infer when the return types mismatch. +- (Unrelated *)initAsUnrelated; +@end + +@interface NSString : NSObject +- (id)init; +- (id)initWithCString:(const char*)string; +@end + +@interface NSArray : NSObject +- (unsigned)count; +@end + +@interface NSBlah +@end + +@interface NSMutableArray : NSArray +@end + +@interface NSBlah () ++ (Unrelated *)newUnrelated; +@end + +void test_inference() { + // Inference based on method family + __typeof__(([[NSString alloc] init])) *str = (NSString**)0; + __typeof__(([[[[NSString new] self] retain] autorelease])) *str2 = (NSString **)0; + __typeof__(([[NSString alloc] initWithCString:"blah"])) *str3 = (NSString**)0; + + // Not inferred + __typeof__(([[NSString new] copy])) *id1 = (id*)0; + + // Not inferred due to instance/class mismatches + __typeof__(([[NSString new] newNotInferred])) *id2 = (id*)0; + __typeof__(([[NSString new] alloc])) *id3 = (id*)0; + __typeof__(([NSString self])) *id4 = (id*)0; + __typeof__(([NSString initWithBlarg])) *id5 = (id*)0; + + // Not inferred due to return type mismatch + __typeof__(([[NSString alloc] initAsUnrelated])) *unrelated = (Unrelated**)0; + __typeof__(([NSBlah newUnrelated])) *unrelated2 = (Unrelated**)0; + + + NSArray *arr = [[NSMutableArray alloc] init]; + NSMutableArray *marr = [arr retain]; // expected-warning{{incompatible pointer types initializing 'NSMutableArray *' with an expression of type 'NSArray *'}} +} + +@implementation NSBlah ++ (Unrelated *)newUnrelated { + return (Unrelated *)0; +} +@end + +@implementation NSBlah (Cat) ++ (Unrelated *)newUnrelated2 { + return (Unrelated *)0; +} +@end + +@interface A +- (id)initBlah; // expected-note 2{{overridden method is part of the 'init' method family}} +@end + +@interface B : A +- (Unrelated *)initBlah; // expected-warning{{method is expected to return an instance of its class type 'B', but is declared to return 'Unrelated *'}} +@end + +@interface C : A +@end + +@implementation C +- (Unrelated *)initBlah { // expected-warning{{method is expected to return an instance of its class type 'C', but is declared to return 'Unrelated *'}} + return (Unrelated *)0; +} +@end + +@interface D ++ (id)newBlarg; // expected-note{{overridden method is part of the 'new' method family}} +@end + +@interface D () ++ alloc; // expected-note{{overridden method is part of the 'alloc' method family}} +@end + +@implementation D ++ (Unrelated *)newBlarg { // expected-warning{{method is expected to return an instance of its class type 'D', but is declared to return 'Unrelated *'}} + return (Unrelated *)0; +} + ++ (Unrelated *)alloc { // expected-warning{{method is expected to return an instance of its class type 'D', but is declared to return 'Unrelated *'}} + return (Unrelated *)0; +} +@end + +@protocol P1 +- (id)initBlah; // expected-note{{overridden method is part of the 'init' method family}} +- (int)initBlarg; +@end + +@protocol P2 <P1> +- (int)initBlah; // expected-warning{{protocol method is expected to return an instance of the implementing class, but is declared to return 'int'}} +- (int)initBlarg; +- (int)initBlech; +@end + +@interface E +- init; +@end + +@implementation E +- init { + return self; +} +@end + +@protocol P3 ++ (NSString *)newString; +@end + +@interface F<P3> +@end + +@implementation F ++ (NSString *)newString { return @"blah"; } +@end + +// <rdar://problem/9340699> +@interface G +- (id)_ABC_init __attribute__((objc_method_family(init))); +@end + +@interface G (Additions) +- (id)_ABC_init2 __attribute__((objc_method_family(init))); +@end + +@implementation G (Additions) +- (id)_ABC_init { + return 0; +} +- (id)_ABC_init2 { + return 0; +} +- (id)_ABC_init3 { + return 0; +} +@end + diff --git a/test/SemaObjCXX/related-result-type-inference.mm b/test/SemaObjCXX/related-result-type-inference.mm new file mode 100644 index 0000000000..58fb961e23 --- /dev/null +++ b/test/SemaObjCXX/related-result-type-inference.mm @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -fobjc-infer-related-result-type -verify %s + +@interface Unrelated +@end + +@interface NSObject ++ (id)new; ++ (id)alloc; +- (NSObject *)init; + +- (id)retain; // expected-note 2{{instance method 'retain' is assumed to return an instance of its receiver type ('NSArray *')}} +- autorelease; + +- (id)self; + +- (id)copy; +- (id)mutableCopy; + +// Do not infer when instance/class mismatches +- (id)newNotInferred; +- (id)alloc; ++ (id)initWithBlarg; ++ (id)self; + +// Do not infer when the return types mismatch. +- (Unrelated *)initAsUnrelated; +@end + +@interface NSString : NSObject +- (id)init; +- (id)initWithCString:(const char*)string; +@end + +@interface NSArray : NSObject +- (unsigned)count; +@end + +@interface NSBlah +@end + +@interface NSMutableArray : NSArray +@end + +@interface NSBlah () ++ (Unrelated *)newUnrelated; +@end + +void test_inference() { + // Inference based on method family + __typeof__(([[NSString alloc] init])) *str = (NSString**)0; + __typeof__(([[[[NSString new] self] retain] autorelease])) *str2 = (NSString **)0; + __typeof__(([[NSString alloc] initWithCString:"blah"])) *str3 = (NSString**)0; + + // Not inferred + __typeof__(([[NSString new] copy])) *id1 = (id*)0; + + // Not inferred due to instance/class mismatches + __typeof__(([[NSString new] newNotInferred])) *id2 = (id*)0; + __typeof__(([[NSString new] alloc])) *id3 = (id*)0; + __typeof__(([NSString self])) *id4 = (id*)0; + __typeof__(([NSString initWithBlarg])) *id5 = (id*)0; + + // Not inferred due to return type mismatch + __typeof__(([[NSString alloc] initAsUnrelated])) *unrelated = (Unrelated**)0; + __typeof__(([NSBlah newUnrelated])) *unrelated2 = (Unrelated**)0; + + NSArray *arr = [[NSMutableArray alloc] init]; + NSMutableArray *marr = [arr retain]; // expected-warning{{incompatible pointer types initializing 'NSMutableArray *' with an expression of type 'NSArray *'}} + marr = [arr retain]; // expected-warning{{incompatible pointer types assigning to 'NSArray *' from 'NSMutableArray *'}} +} |