diff --git a/.gitattributes b/.gitattributes index 1dc22b533a..f03f7df3eb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15415,6 +15415,7 @@ tests/test/units/classes/tstringlistexchange.pp svneol=native#text/pascal tests/test/units/classes/ttbits.pp svneol=native#text/pascal tests/test/units/classes/ttlist.pp svneol=native#text/plain tests/test/units/classes/tvclcomobject.pp svneol=native#text/plain +tests/test/units/cocoaall/tw35994.pp svneol=native#text/plain tests/test/units/cpu/tcpu1.pp svneol=native#text/pascal tests/test/units/crt/tcrt.pp svneol=native#text/plain tests/test/units/crt/tctrlc.pp svneol=native#text/plain diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas index c4b4059404..67eeca3bc0 100644 --- a/compiler/defcmp.pas +++ b/compiler/defcmp.pas @@ -2505,6 +2505,13 @@ implementation exit; end; + if (realself.objecttype in [odt_objcclass,odt_objcprotocol]) and + (otherdef=objc_idtype) then + begin + result:=true; + exit; + end; + if (otherdef.typ<>objectdef) then begin result:=false; diff --git a/compiler/ncal.pas b/compiler/ncal.pas index 79c15018d3..61d95f9a21 100644 --- a/compiler/ncal.pas +++ b/compiler/ncal.pas @@ -3864,6 +3864,20 @@ implementation exit; end; + { in case this is an Objective-C message that returns a related object type by convention, + override the default result type } + if po_objc_related_result_type in procdefinition.procoptions then + begin + { don't crash in case of syntax errors } + if assigned(methodpointer) then + begin + include(callnodeflags,cnf_typedefset); + typedef:=methodpointer.resultdef; + if typedef.typ=classrefdef then + typedef:=tclassrefdef(typedef).pointeddef; + end; + end; + { ensure that the result type is set } if not(cnf_typedefset in callnodeflags) then begin diff --git a/compiler/pdecl.pas b/compiler/pdecl.pas index a7041e4312..a97ad6399f 100644 --- a/compiler/pdecl.pas +++ b/compiler/pdecl.pas @@ -539,6 +539,52 @@ implementation consume(_RECKKLAMMER); end; + + { From http://clang.llvm.org/docs/LanguageExtensions.html#objective-c-features : + To determine whether a method has an inferred related result type, the first word in the camel-case selector + (e.g., “init” in “initWithObjects”) is considered, and the method will have a related result type if its return + type is compatible with the type of its class and if: + * the first word is "alloc" or "new", and the method is a class method, or + * the first word is "autorelease", "init", "retain", or "self", and the method is an instance method. + + 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. + } + procedure pd_set_objc_related_result(def: tobject; para: pointer); + var + pd: tprocdef; + i, firstcamelend: longint; + inferresult: boolean; + begin + if tdef(def).typ<>procdef then + exit; + pd:=tprocdef(def); + if not(po_msgstr in pd.procoptions) then + internalerror(2019082401); + firstcamelend:=length(pd.messageinf.str^); + for i:=1 to length(pd.messageinf.str^) do + if pd.messageinf.str^[i] in ['A'..'Z'] then + begin + firstcamelend:=pred(i); + break; + end; + case copy(pd.messageinf.str^,1,firstcamelend) of + 'alloc', + 'new': + inferresult:=po_classmethod in pd.procoptions; + 'autorelease', + 'init', + 'retain', + 'self': + inferresult:=not(po_classmethod in pd.procoptions); + else + inferresult:=false; + end; + if inferresult and + def_is_related(tdef(pd.procsym.owner.defowner),pd.returndef) then + include(pd.procoptions,po_objc_related_result_type); + end; + procedure types_dec(in_structure: boolean;out had_generic:boolean;var rtti_attrs_def: trtti_attribute_list); function determine_generic_def(name:tidstring):tstoreddef; @@ -1057,7 +1103,10 @@ implementation if is_objc_class_or_protocol(hdef) and (not is_objccategory(hdef) or assigned(tobjectdef(hdef).childof)) then - tobjectdef(hdef).finish_objc_data; + begin + tobjectdef(hdef).finish_objc_data; + tobjectdef(hdef).symtable.DefList.ForEachCall(@pd_set_objc_related_result,nil); + end; if is_cppclass(hdef) then tobjectdef(hdef).finish_cpp_data; diff --git a/compiler/symconst.pas b/compiler/symconst.pas index bc7d0caba9..cfbe00b23f 100644 --- a/compiler/symconst.pas +++ b/compiler/symconst.pas @@ -420,7 +420,9 @@ type po_noinline, { same as po_varargs, but with an array-of-const parameter instead of with the "varargs" modifier or Mac-Pascal ".." parameter } - po_variadic + po_variadic, + { implicitly return same type as the class instance to which the message is sent } + po_objc_related_result_type ); tprocoptions=set of tprocoption; @@ -1064,7 +1066,8 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has 'po_is_auto_getter',{po_is_auto_getter} 'po_is_auto_setter',{po_is_auto_setter} 'po_noinline',{po_noinline} - 'C-style array-of-const' {po_variadic} + 'C-style array-of-const', {po_variadic} + 'objc-related-result-type' {po_objc_related_result_type} ); implementation diff --git a/compiler/utils/ppuutils/ppudump.pp b/compiler/utils/ppuutils/ppudump.pp index 4a648ce950..f162dc5cbc 100644 --- a/compiler/utils/ppuutils/ppudump.pp +++ b/compiler/utils/ppuutils/ppudump.pp @@ -2951,7 +2951,8 @@ const (mask:po_is_auto_getter; str: 'Automatically generated getter'), (mask:po_is_auto_setter; str: 'Automatically generated setter'), (mask:po_noinline; str: 'Never inline'), - (mask:po_variadic; str: 'C VarArgs with array-of-const para') + (mask:po_variadic; str: 'C VarArgs with array-of-const para'), + (mask:po_objc_related_result_type; str: 'Objective-C related result type') ); var proctypeoption : tproctypeoption; diff --git a/tests/test/units/cocoaall/tw35994.pp b/tests/test/units/cocoaall/tw35994.pp new file mode 100644 index 0000000000..af16d142e7 --- /dev/null +++ b/tests/test/units/cocoaall/tw35994.pp @@ -0,0 +1,31 @@ + +{$MODE OBJFPC} +{$MODESWITCH OBJECTIVEC1} + +program test; + +uses + CocoaAll; + +var + obj: NSObject; + path: NSString; + dict: NSDictionary; + mDict: NSMutableDictionary; + pool: NSAutoReleasePool; +begin + pool := NSAutoReleasePool.alloc.init; + obj := NSObject.alloc.init; + + path := NSSTR(''); + dict := NSDictionary.dictionaryWithContentsOfFile(path); + dict := NSDictionary.alloc.initWithContentsOfFile(path); // ERROR: got "NSArray" expected "NSDictionary" + dict := NSDictionary(NSDictionary.alloc).initWithContentsOfFile(path); + + dict := NSMutableDictionary.dictionaryWithContentsOfFile(path); + mDict := NSMutableDictionary.dictionaryWithContentsOfFile(path); // ERROR: got "NSDictionary" expected "NSMutableDictionary" + dict := NSMutableDictionary.alloc.initWithContentsOfFile(path); // ERROR: got "NSArray" expected "NSDictionary" + mDict := NSMutableDictionary.alloc.initWithContentsOfFile(path); // ERROR: got "NSArray" expected "NSDictionary" + + pool.release; +end.