Procházet zdrojové kódy

+ support for Objective-Pascal for-in loops ("fast enumerations")
+ {$modeswitch objectivec2}, which is required before you can use
Objective-C 2.0 features (such as the above). It automatically
also implies {$modeswitch objectivec1}
+ genloadfield() helper to load a field of a node representing
a record/object/class

git-svn-id: trunk@15460 -

Jonas Maebe před 15 roky
rodič
revize
835899524b

+ 2 - 0
.gitattributes

@@ -9294,6 +9294,8 @@ tests/test/tobjc31.pp svneol=native#text/plain
 tests/test/tobjc32.pp svneol=native#text/plain
 tests/test/tobjc32a.pp svneol=native#text/plain
 tests/test/tobjc32b.pp svneol=native#text/plain
+tests/test/tobjc33.pp svneol=native#text/plain
+tests/test/tobjc33a.pp svneol=native#text/plain
 tests/test/tobjc4.pp svneol=native#text/plain
 tests/test/tobjc4a.pp svneol=native#text/plain
 tests/test/tobjc5.pp svneol=native#text/plain

+ 4 - 2
compiler/globtype.pas

@@ -270,7 +270,8 @@ interface
          m_property,            { allow properties }
          m_default_inline,      { allow inline proc directive }
          m_except,              { allow exception-related keywords }
-         m_objectivec1          { support interfacing with Objective-C (1.0) }
+         m_objectivec1,         { support interfacing with Objective-C (1.0) }
+         m_objectivec2          { support interfacing with Objective-C (2.0) }
        );
        tmodeswitches = set of tmodeswitch;
 
@@ -387,7 +388,8 @@ interface
          'PROPERTIES',
          'ALLOWINLINE',
          'EXCEPTIONS',
-         'OBJECTIVEC1');
+         'OBJECTIVEC1',
+         'OBJECTIVEC2');
 
 
      type

+ 11 - 1
compiler/msg/errore.msg

@@ -368,7 +368,7 @@ scanner_w_illegal_warn_identifier=02087_W_Illegal identifier "$1" for $WARN dire
 #
 # Parser
 #
-# 03293 is the last used one
+# 03295 is the last used one
 #
 % \section{Parser messages}
 % This section lists all parser messages. The parser takes care of the
@@ -1315,6 +1315,16 @@ parser_e_widestring_to_ansi_compile_time=03293_E_Unicodechar/string constants ca
 % constant expressions that have to be converted into an ansistring or shortstring
 % at compile time, for example inside typed constants. The reason is that the
 % compiler cannot know what the actual ansi encoding will be at run time.
+parser_e_objc_enumerator_2_0=03294_E_For-in Objective-Pascal loops require \{\$modeswitch ObjectiveC1\} to be active
+% Objective-C ``fast enumeration'' support was added in Objective-C 2.0, and
+% hence the appropriate modeswitch has to be activated to expose this feature.
+% Note that Objective-C 2.0 programs require Mac OS X 10.5 or later.
+parser_e_objc_missing_enumeration_defs=03295_E_The compiler cannot find the NSFastEnumerationProtocol or NSFastEnumerationState type in the CocoaAll unit
+% Objective-C for-in loops (fast enumeration) require that the compiler can
+% find a unit called CocoaAll that contains definitions for the
+% NSFastEnumerationProtocol and NSFastEnumerationState types. If you get this
+% error, most likely the compiler is finding and loading an alternate CocoaAll
+% unit.
 #
 # Type Checking
 #

+ 4 - 2
compiler/msgidx.inc

@@ -382,6 +382,8 @@ const
   parser_e_no_paras_for_class_destructor=03291;
   parser_f_modeswitch_objc_required=03292;
   parser_e_widestring_to_ansi_compile_time=03293;
+  parser_e_objc_enumerator_2_0=03294;
+  parser_e_objc_missing_enumeration_defs=03295;
   type_e_mismatch=04000;
   type_e_incompatible_types=04001;
   type_e_not_equal_types=04002;
@@ -860,9 +862,9 @@ const
   option_info=11024;
   option_help_pages=11025;
 
-  MsgTxtSize = 56492;
+  MsgTxtSize = 56695;
 
   MsgIdxMax : array[1..20] of longint=(
-    24,88,294,96,80,51,110,22,202,63,
+    24,88,296,96,80,51,110,22,202,63,
     49,20,1,1,1,1,1,1,1,1
   );

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 266 - 261
compiler/msgtxt.inc


+ 1 - 1
compiler/ncal.pas

@@ -1859,7 +1859,7 @@ implementation
         if (cnf_inherited in callnodeflags) then
           begin
              block:=internalstatements(statements);
-             objcsupertype:=search_named_unit_globaltype('OBJC','OBJC_SUPER').typedef;
+             objcsupertype:=search_named_unit_globaltype('OBJC','OBJC_SUPER',true).typedef;
              if (objcsupertype.typ<>recorddef) then
                internalerror(2009032901);
              { temp for the for the objc_super record }

+ 270 - 30
compiler/nflw.pas

@@ -251,6 +251,232 @@ implementation
       end;
 
 
+    function create_objc_for_in_loop(hloopvar, hloopbody, expr: tnode): tnode;
+      var
+        mainstatement, outerloopbodystatement, innerloopbodystatement, tempstatement: tstatementnode;
+        state, mutationcheck, currentamount, innerloopcounter, items, expressiontemp: ttempcreatenode;
+        outerloop, innerloop, hp: tnode;
+        itemsarraydef: tarraydef;
+        sym: tsym;
+      begin
+        { Objective-C enumerators require Objective-C 2.0 }
+        if not(m_objectivec2 in current_settings.modeswitches) then
+          begin
+            result:=cerrornode.create;
+            MessagePos(expr.fileinfo,parser_e_objc_enumerator_2_0);
+            exit;
+          end;
+        { Requires the NSFastEnumeration protocol and NSFastEnumerationState
+          record }
+        maybeloadcocoatypes;
+        if not assigned(objc_fastenumeration) or
+           not assigned(objc_fastenumerationstate) then
+          begin
+            result:=cerrornode.create;
+            MessagePos(expr.fileinfo,parser_e_objc_missing_enumeration_defs);
+            exit;
+          end;
+
+        (* Original code:
+            for hloopvar in expression do
+              <hloopbody>
+
+          Pascal code equivalent into which it has to be transformed
+          (sure would be nice if the compiler had some kind of templates ;) :
+            var
+              state: NSFastEnumerationState;
+              expressiontemp: NSFastEnumerationProtocol;
+              mutationcheck,
+              currentamount,
+              innerloopcounter: culong;
+              { size can be increased/decreased if desired }
+              items: array[1..16] of id;
+            begin
+              fillchar(state,sizeof(state),0);
+              expressiontemp:=expression;
+              repeat
+                currentamount:=expressiontemp.countByEnumeratingWithState_objects_count(@state,@items,length(items));
+                if currentamount=0 then
+                  begin
+                    { "The iterating variable is set to nil when the loop ends by
+                      exhausting the source pool of objects" }
+                    hloopvar:=nil;
+                    break;
+                  end;
+                mutationcheck:=state.mutationsptr^;
+                innerloopcounter:=culong(-1);
+                repeat
+                  { at the start so that "continue" in <loopbody> works correctly }
+                  { don't use for-loop, because then the value of the iteration
+                    counter is undefined on exit and we have to check it in the
+                    outer repeat/until condition }
+                  {$push}
+                  {$r-,q-}
+                  inc(innerloopcounter);
+                  {$pop}
+                  if innerloopcounter=currentamount then
+                    break;
+                  if mutationcheck<>state.mutationsptr^ then
+                    { raises Objective-C exception... }
+                    objc_enumerationMutation(expressiontemp);
+                  hloopvar:=state.itemsPtr[innerloopcounter];
+                  { if continue in loopbody -> jumps to start, increases count and checks }
+                  { if break in loopbody: goes to outer repeat/until and innerloopcount
+                    will be < currentamount -> stops }
+                  <hloopbody>
+                until false;
+              { if the inner loop terminated early, "break" was used and we have
+                to stop }
+              { "If the loop is terminated early, the iterating variable is left
+                pointing to the last iteration item." }
+              until innerloopcounter<currentamount;
+            end;
+         *)
+
+         result:=internalstatements(mainstatement);
+         { the fast enumeration state }
+         state:=ctempcreatenode.create(objc_fastenumerationstate,objc_fastenumerationstate.size,tt_persistent,false);
+         typecheckpass(tnode(state));
+         addstatement(mainstatement,state);
+         { the temporary items array }
+         itemsarraydef:=tarraydef.create(1,16,u32inttype);
+         itemsarraydef.elementdef:=objc_idtype;
+         items:=ctempcreatenode.create(itemsarraydef,itemsarraydef.size,tt_persistent,false);
+         addstatement(mainstatement,items);
+         typecheckpass(tnode(items));
+         { temp for the expression/collection through which we iterate }
+         expressiontemp:=ctempcreatenode.create(objc_fastenumeration,objc_fastenumeration.size,tt_persistent,true);
+         addstatement(mainstatement,expressiontemp);
+         { currentamount temp (not really clean: we use ptruint instead of
+           culong) }
+         currentamount:=ctempcreatenode.create(ptruinttype,ptruinttype.size,tt_persistent,true);
+         typecheckpass(tnode(currentamount));
+         addstatement(mainstatement,currentamount);
+         { mutationcheck temp (idem) }
+         mutationcheck:=ctempcreatenode.create(ptruinttype,ptruinttype.size,tt_persistent,true);
+         typecheckpass(tnode(mutationcheck));
+         addstatement(mainstatement,mutationcheck);
+         { innerloopcounter temp (idem) }
+         innerloopcounter:=ctempcreatenode.create(ptruinttype,ptruinttype.size,tt_persistent,true);
+         typecheckpass(tnode(innerloopcounter));
+         addstatement(mainstatement,innerloopcounter);
+         { initialise the state with 0 }
+         addstatement(mainstatement,ccallnode.createinternfromunit('SYSTEM','FILLCHAR',
+           ccallparanode.create(genintconstnode(0),
+             ccallparanode.create(genintconstnode(objc_fastenumerationstate.size),
+               ccallparanode.create(ctemprefnode.create(state),nil)
+             )
+           )
+         ));
+         { this will also check whether the expression (potentially) conforms
+           to the NSFastEnumeration protocol (use expr.getcopy, because the
+           caller will free expr) }
+         addstatement(mainstatement,cassignmentnode.create(ctemprefnode.create(expressiontemp),expr.getcopy));
+
+         { we add the "repeat..until" afterwards, now just create the body }
+         outerloop:=internalstatements(outerloopbodystatement);
+         { the countByEnumeratingWithState_objects_count call }
+         hp:=ccallparanode.create(cinlinenode.create(in_length_x,false,ctypenode.create(itemsarraydef)),
+               ccallparanode.create(caddrnode.create(ctemprefnode.create(items)),
+                 ccallparanode.create(caddrnode.create(ctemprefnode.create(state)),nil)
+               )
+             );
+         sym:=search_class_member(objc_fastenumeration,'COUNTBYENUMERATINGWITHSTATE_OBJECTS_COUNT');
+         if not assigned(sym) or
+            (sym.typ<>procsym) then
+           internalerror(2010061901);
+         hp:=ccallnode.create(hp,tprocsym(sym),sym.owner,ctemprefnode.create(expressiontemp),[]);
+         addstatement(outerloopbodystatement,cassignmentnode.create(
+           ctemprefnode.create(currentamount),hp));
+         { if currentamount = 0, bail out (use copy of hloopvar, because we
+           have to use it again below) }
+         hp:=internalstatements(tempstatement);
+         addstatement(tempstatement,cassignmentnode.create(
+             hloopvar.getcopy,cnilnode.create));
+         addstatement(tempstatement,cbreaknode.create);
+         addstatement(outerloopbodystatement,cifnode.create(
+           caddnode.create(equaln,ctemprefnode.create(currentamount),genintconstnode(0)),
+           hp,nil));
+        { initial value of mutationcheck }
+        hp:=ctemprefnode.create(state);
+        typecheckpass(hp);
+        hp:=cderefnode.create(genloadfield(hp,'MUTATIONSPTR'));
+        addstatement(outerloopbodystatement,cassignmentnode.create(
+          ctemprefnode.create(mutationcheck),hp));
+        { initialise innerloopcounter }
+        addstatement(outerloopbodystatement,cassignmentnode.create(
+          ctemprefnode.create(innerloopcounter),cordconstnode.create(-1,ptruinttype,false)));
+
+        { and now the inner loop, again adding the repeat/until afterwards }
+        innerloop:=internalstatements(innerloopbodystatement);
+        { inc(innerloopcounter) without range/overflowchecking (because
+          we go from culong(-1) to 0 during the first iteration }
+        hp:=cinlinenode.create(
+          in_inc_x,false,ccallparanode.create(
+            ctemprefnode.create(innerloopcounter),nil));
+        hp.localswitches:=hp.localswitches-[cs_check_range,cs_check_overflow];
+        addstatement(innerloopbodystatement,hp);
+        { if innerloopcounter=currentamount then break to the outer loop }
+        addstatement(innerloopbodystatement,cifnode.create(
+          caddnode.create(equaln,
+            ctemprefnode.create(innerloopcounter),
+            ctemprefnode.create(currentamount)),
+          cbreaknode.create,
+          nil));
+        { verify that the collection didn't change in the mean time }
+        hp:=ctemprefnode.create(state);
+        typecheckpass(hp);
+        addstatement(innerloopbodystatement,cifnode.create(
+          caddnode.create(unequaln,
+            ctemprefnode.create(mutationcheck),
+            cderefnode.create(genloadfield(hp,'MUTATIONSPTR'))
+          ),
+          ccallnode.createinternfromunit('OBJC','OBJC_ENUMERATIONMUTATION',
+            ccallparanode.create(ctemprefnode.create(expressiontemp),nil)),
+          nil));
+        { finally: actually get the next element }
+        hp:=ctemprefnode.create(state);
+        typecheckpass(hp);
+        hp:=genloadfield(hp,'ITEMSPTR');
+        typecheckpass(hp);
+        { don't simply use a vecn, because indexing a pointer won't work in
+          non-FPC modes }
+        if hp.resultdef.typ<>pointerdef then
+          internalerror(2010061904);
+        inserttypeconv(hp,
+          tarraydef.create_from_pointer(tpointerdef(hp.resultdef).pointeddef));
+        hp:=cvecnode.create(hp,ctemprefnode.create(innerloopcounter));
+        addstatement(innerloopbodystatement,
+          cassignmentnode.create(hloopvar,hp));
+        { the actual loop body! }
+        addstatement(innerloopbodystatement,hloopbody);
+
+        { create the inner repeat/until and add it to the body of the outer
+          one }
+        hp:=cwhilerepeatnode.create(
+          { repeat .. until false }
+          cordconstnode.create(0,booltype,false),innerloop,false,true);
+        addstatement(outerloopbodystatement,hp);
+
+        { create the outer repeat/until and add it to the the main body }
+        hp:=cwhilerepeatnode.create(
+          { repeat .. until innerloopcounter<currentamount }
+          caddnode.create(ltn,
+            ctemprefnode.create(innerloopcounter),
+            ctemprefnode.create(currentamount)),
+          outerloop,false,true);
+        addstatement(mainstatement,hp);
+
+        { release the temps }
+        addstatement(mainstatement,ctempdeletenode.create(state));
+        addstatement(mainstatement,ctempdeletenode.create(mutationcheck));
+        addstatement(mainstatement,ctempdeletenode.create(currentamount));
+        addstatement(mainstatement,ctempdeletenode.create(innerloopcounter));
+        addstatement(mainstatement,ctempdeletenode.create(items));
+        addstatement(mainstatement,ctempdeletenode.create(expressiontemp));
+      end;
+
+
     function create_string_for_in_loop(hloopvar, hloopbody, expr: tnode): tnode;
       var
         loopstatement, loopbodystatement: tstatementnode;
@@ -616,50 +842,64 @@ implementation
         else
           begin
             { loop is made for an expression }
-            // search for operator first
-            pd:=search_enumerator_operator(expr.resultdef);
-            // if there is no operator then search for class/object enumerator method
-            if (pd=nil) and (expr.resultdef.typ=objectdef) then
-              pd:=tobjectdef(expr.resultdef).search_enumerator_get;
-            if pd<>nil then
+            // Objective-C uses different conventions (and it's only supported for Objective-C 2.0)
+            if is_objc_class_or_protocol(hloopvar.resultdef) or
+               is_objc_class_or_protocol(expr.resultdef) then
               begin
-                // seach movenext and current symbols
-                movenext:=tobjectdef(pd.returndef).search_enumerator_move;
-                if movenext = nil then
+                result:=create_objc_for_in_loop(hloopvar,hloopbody,expr);
+                if result.nodetype=errorn then
                   begin
-                    result:=cerrornode.create;
                     hloopvar.free;
                     hloopbody.free;
-                    MessagePos1(expr.fileinfo,sym_e_no_enumerator_move,pd.returndef.GetTypeName);
-                  end
-                else
+                  end;
+              end
+            else
+              begin
+                // search for operator first
+                pd:=search_enumerator_operator(expr.resultdef);
+                // if there is no operator then search for class/object enumerator method
+                if (pd=nil) and (expr.resultdef.typ=objectdef) then
+                  pd:=tobjectdef(expr.resultdef).search_enumerator_get;
+                if pd<>nil then
                   begin
-                    current:=tpropertysym(tobjectdef(pd.returndef).search_enumerator_current);
-                    if current = nil then
+                    // seach movenext and current symbols
+                    movenext:=tobjectdef(pd.returndef).search_enumerator_move;
+                    if movenext = nil then
                       begin
                         result:=cerrornode.create;
                         hloopvar.free;
                         hloopbody.free;
-                        MessagePos1(expr.fileinfo,sym_e_no_enumerator_current,pd.returndef.GetTypeName);
+                        MessagePos1(expr.fileinfo,sym_e_no_enumerator_move,pd.returndef.GetTypeName);
                       end
                     else
-                      result:=create_enumerator_for_in_loop(hloopvar, hloopbody, expr, pd, movenext, current);
-                  end;
-              end
-            else
-              begin
-                case expr.resultdef.typ of
-                  stringdef: result:=create_string_for_in_loop(hloopvar, hloopbody, expr);
-                  arraydef: result:=create_array_for_in_loop(hloopvar, hloopbody, expr);
-                  setdef: result:=create_set_for_in_loop(hloopvar, hloopbody, expr);
+                      begin
+                        current:=tpropertysym(tobjectdef(pd.returndef).search_enumerator_current);
+                        if current = nil then
+                          begin
+                            result:=cerrornode.create;
+                            hloopvar.free;
+                            hloopbody.free;
+                            MessagePos1(expr.fileinfo,sym_e_no_enumerator_current,pd.returndef.GetTypeName);
+                          end
+                        else
+                          result:=create_enumerator_for_in_loop(hloopvar, hloopbody, expr, pd, movenext, current);
+                      end;
+                  end
                 else
                   begin
-                    result:=cerrornode.create;
-                    hloopvar.free;
-                    hloopbody.free;
-                    MessagePos1(expr.fileinfo,sym_e_no_enumerator,expr.resultdef.GetTypeName);
+                    case expr.resultdef.typ of
+                      stringdef: result:=create_string_for_in_loop(hloopvar, hloopbody, expr);
+                      arraydef: result:=create_array_for_in_loop(hloopvar, hloopbody, expr);
+                      setdef: result:=create_set_for_in_loop(hloopvar, hloopbody, expr);
+                    else
+                      begin
+                        result:=cerrornode.create;
+                        hloopvar.free;
+                        hloopbody.free;
+                        MessagePos1(expr.fileinfo,sym_e_no_enumerator,expr.resultdef.GetTypeName);
+                      end;
+                    end;
                   end;
-                end;
               end;
           end;
         current_filepos:=storefilepos;

+ 17 - 0
compiler/nutils.pas

@@ -96,6 +96,11 @@ interface
       bitpacked structure }
     function is_bitpacked_access(n: tnode): boolean;
 
+    { creates a load of field 'fieldname' in the record/class/...
+      represented by n; assumes the resultdef of n is set }
+    function genloadfield(n: tnode; const fieldname: string): tnode;
+
+
 implementation
 
     uses
@@ -1095,6 +1100,18 @@ implementation
       end;
 
 
+    function genloadfield(n: tnode; const fieldname: string): tnode;
+      var
+        vs         : tsym;
+      begin
+        vs:=tsym(tabstractrecorddef(n.resultdef).symtable.find(fieldname));
+        if not assigned(vs) or
+           (vs.typ<>fieldvarsym) then
+          internalerror(2010061902);
+        result:=csubscriptnode.create(vs,n);
+      end;
+
+
     function has_no_code(n : tnode) : boolean;
       begin
         if n=nil then

+ 5 - 5
compiler/objcgutl.pas

@@ -308,7 +308,7 @@ procedure tobjcrttiwriter.gen_objc_methods(list: tasmlist; objccls: tobjectdef;
     else
       begin
         { size of each entry -- always 32 bit value }
-        mtype:=search_named_unit_globaltype('OBJC','OBJC_METHOD').typedef;
+        mtype:=search_named_unit_globaltype('OBJC','OBJC_METHOD',true).typedef;
         list.Concat(tai_const.Create_32bit(mtype.size));
       end;
     { number of objc_method entries in the method_list array -- always 32 bit}
@@ -411,7 +411,7 @@ begin
   if (abi=oa_nonfragile) then
     begin
       { size of each entry -- always 32 bit value }
-      mtype:=search_named_unit_globaltype('OBJC','OBJC_METHOD').typedef;
+      mtype:=search_named_unit_globaltype('OBJC','OBJC_METHOD',true).typedef;
       list.Concat(tai_const.Create_32bit(mtype.size));
     end;
   list.Concat(Tai_const.Create_32bit(items.count));
@@ -1001,7 +1001,7 @@ procedure tobjcrttiwriter_nonfragile.gen_objc_ivars(list: tasmlist; objccls: tob
     list.Concat(tai_label.Create(ivarslabel));
 
     { size of each entry -- always 32 bit value }
-    ivtype:=search_named_unit_globaltype('OBJC','OBJC_IVAR').typedef;
+    ivtype:=search_named_unit_globaltype('OBJC','OBJC_IVAR',true).typedef;
     list.concat(tai_const.Create_32bit(ivtype.size));
     { number of entries -- always 32 bit value }
     list.Concat(tai_const.Create_32bit(vcnt));
@@ -1129,7 +1129,7 @@ procedure tobjcrttiwriter_nonfragile.gen_objc_protocol(list: tasmlist; protocol:
     { TODO: properties }
     list.Concat(Tai_const.Create_pint(0));
     { size of this type }
-    prottype:=search_named_unit_globaltype('OBJC','OBJC_PROTOCOL').typedef;
+    prottype:=search_named_unit_globaltype('OBJC','OBJC_PROTOCOL',true).typedef;
     list.concat(tai_const.Create_32bit(prottype.size));
     { flags }
     list.concat(tai_const.Create_32bit(0));
@@ -1330,7 +1330,7 @@ procedure tobjcrttiwriter_nonfragile.gen_objc_class_ro_part(list: tasmlist; objc
         flags:=flags or CLS_META;
         rttitype:=objcmetarortti;
         { metaclass size/start: always size of objc_object }
-        class_type:=search_named_unit_globaltype('OBJC','OBJC_OBJECT').typedef;
+        class_type:=search_named_unit_globaltype('OBJC','OBJC_OBJECT',true).typedef;
         start:=class_type.size;
         size:=start;
       end

+ 1 - 1
compiler/pmodules.pas

@@ -689,7 +689,7 @@ implementation
         { Macpas unit? }
         if m_mac in current_settings.modeswitches then
           AddUnit('macpas');
-        { Objective-C 1.0 support unit? }
+        { Objective-C support unit? }
         if (m_objectivec1 in current_settings.modeswitches) then
           begin
             { interface to Objective-C run time }

+ 10 - 7
compiler/scanner.pas

@@ -467,12 +467,9 @@ implementation
         for i:=m_class to high(tmodeswitch) do
           if s=modeswitchstr[i] then
             begin
-              { Objective-C is currently only supported for 32 bit Darwin targets
-                (and Objective-C 2.0 will be required for 64 bit ones)
-                Not yet tested for ARM either.
-              }
+              { Objective-C is currently only supported for Darwin targets }
               if doinclude and
-                 (i=m_objectivec1) and
+                 (i in [m_objectivec1,m_objectivec2]) and
                  not(target_info.system in systems_objc_supported) then
                 begin
                   Message1(option_unsupported_target_for_feature,'Objective-C');
@@ -485,13 +482,19 @@ implementation
               if doinclude then
                 begin
                   include(current_settings.modeswitches,i);
-                  if (i=m_objectivec1) then
+                  { Objective-C 2.0 support implies 1.0 support }
+                  if (i=m_objectivec2) then
+                    include(current_settings.modeswitches,m_objectivec1);
+                  if (i in [m_objectivec1,m_objectivec2]) then
                     include(current_settings.modeswitches,m_class);
                 end
               else
                 begin
                   exclude(current_settings.modeswitches,i);
-                  if (i=m_objectivec1) and
+                  { Objective-C 2.0 support implies 1.0 support }
+                  if (i=m_objectivec2) then
+                    exclude(current_settings.modeswitches,m_objectivec1);
+                  if (i in [m_objectivec1,m_objectivec2]) and
                      ([m_delphi,m_objfpc]*current_settings.modeswitches=[]) then
                     exclude(current_settings.modeswitches,m_class);
                 end;

+ 31 - 8
compiler/symdef.pas

@@ -700,10 +700,13 @@ interface
        objc_metaclasstype,
        objc_superclasstype,
        objc_idtype,
-       objc_seltype         : tpointerdef;
-       objc_objecttype      : trecorddef;
+       objc_seltype              : tpointerdef;
+       objc_objecttype           : trecorddef;
        { base type of @protocol(protocolname) Objective-C statements }
-       objc_protocoltype    : tobjectdef;
+       objc_protocoltype         : tobjectdef;
+       { helper types for for-in "fast enumeration" support in Objective-C 2.0 }
+       objc_fastenumeration      : tobjectdef;
+       objc_fastenumerationstate : trecorddef;
 
     const
 {$ifdef i386}
@@ -767,6 +770,7 @@ interface
     function is_class_or_object(def: tdef): boolean;
 
     procedure loadobjctypes;
+    procedure maybeloadcocoatypes;
 
     function use_vectorfpu(def : tdef) : boolean;
 
@@ -5387,11 +5391,30 @@ implementation
 
     procedure loadobjctypes;
       begin
-        objc_metaclasstype:=tpointerdef(search_named_unit_globaltype('OBJC','POBJC_CLASS').typedef);
-        objc_superclasstype:=tpointerdef(search_named_unit_globaltype('OBJC','POBJC_SUPER').typedef);
-        objc_idtype:=tpointerdef(search_named_unit_globaltype('OBJC','ID').typedef);
-        objc_seltype:=tpointerdef(search_named_unit_globaltype('OBJC','SEL').typedef);
-        objc_objecttype:=trecorddef(search_named_unit_globaltype('OBJC','OBJC_OBJECT').typedef);
+        objc_metaclasstype:=tpointerdef(search_named_unit_globaltype('OBJC','POBJC_CLASS',true).typedef);
+        objc_superclasstype:=tpointerdef(search_named_unit_globaltype('OBJC','POBJC_SUPER',true).typedef);
+        objc_idtype:=tpointerdef(search_named_unit_globaltype('OBJC','ID',true).typedef);
+        objc_seltype:=tpointerdef(search_named_unit_globaltype('OBJC','SEL',true).typedef);
+        objc_objecttype:=trecorddef(search_named_unit_globaltype('OBJC','OBJC_OBJECT',true).typedef);
+      end;
+
+
+    procedure maybeloadcocoatypes;
+      var
+        tsym: ttypesym;
+      begin
+        if assigned(objc_fastenumeration) then
+          exit;
+        tsym:=search_named_unit_globaltype('COCOAALL','NSFASTENUMERATIONPROTOCOL',false);
+        if assigned(tsym) then
+          objc_fastenumeration:=tobjectdef(tsym.typedef)
+        else
+          objc_fastenumeration:=nil;
+        tsym:=search_named_unit_globaltype('COCOAALL','NSFASTENUMERATIONSTATE',false);
+        if assigned(tsym) then
+          objc_fastenumerationstate:=trecorddef(tsym.typedef)
+        else
+        objc_fastenumerationstate:=nil;
       end;
 
 

+ 4 - 3
compiler/symtable.pas

@@ -209,7 +209,7 @@ interface
     function  searchsym_in_class_by_msgint(classh:tobjectdef;msgid:longint;out srdef : tdef;out srsym:tsym;out srsymtable:TSymtable):boolean;
     function  searchsym_in_class_by_msgstr(classh:tobjectdef;const s:string;out srsym:tsym;out srsymtable:TSymtable):boolean;
     function  search_system_type(const s: TIDString): ttypesym;
-    function  search_named_unit_globaltype(const unitname, typename: TIDString): ttypesym;
+    function  search_named_unit_globaltype(const unitname, typename: TIDString; throwerror: boolean): ttypesym;
     function  search_class_member(pd : tobjectdef;const s : string):tsym;
     function  search_assignment_operator(from_def,to_def:Tdef):Tprocdef;
     function  search_enumerator_operator(type_def:Tdef):Tprocdef;
@@ -2194,7 +2194,7 @@ implementation
       end;
 
 
-    function search_named_unit_globaltype(const unitname, typename: TIDString): ttypesym;
+    function search_named_unit_globaltype(const unitname, typename: TIDString; throwerror: boolean): ttypesym;
       var
         srsymtable: tsymtable;
         sym: tsym;
@@ -2207,7 +2207,8 @@ implementation
           end
         else
           begin
-            cgmessage2(cg_f_unknown_type_in_unit,typename,unitname);
+            if throwerror then
+              cgmessage2(cg_f_unknown_type_in_unit,typename,unitname);
             result:=nil;
           end;
       end;

+ 42 - 0
tests/test/tobjc33.pp

@@ -0,0 +1,42 @@
+{ %target=darwin }
+{ %cpu=powerpc,powerpc64,i386,x86_64,arm }
+
+{ Written by Jonas Maebe in 2010, released into the public domain }
+
+{$mode delphi}
+{$modeswitch objectivec2}
+
+uses
+  CocoaAll;
+
+var
+  arr: NSMutableArray;
+  element: NSString;
+  pool: NSAutoreleasePool;
+  i: longint;
+begin
+  pool:=NSAutoreleasePool.alloc.init;
+  arr:=NSMutableArray.arrayWithObjects(
+    NSSTR('One'),
+    NSSTR('Two'),
+    NSSTR('Three'),
+    NSSTR('Four'),
+    NSSTR('Five'),
+    NSSTR('Six'),
+    NSSTR('Seven'),
+    nil);
+
+  i:=0;
+  for element in arr do
+    begin
+      inc(i);
+      if i=2 then
+        continue;
+      if i=5 then
+        break;
+      if i in [2,5..10] then
+        halt(1);
+      NSLog(NSSTR('element: %@'),element);
+    end;
+  pool.release;
+end.

+ 43 - 0
tests/test/tobjc33a.pp

@@ -0,0 +1,43 @@
+{ %fail }
+{ %target=darwin }
+{ %cpu=powerpc,powerpc64,i386,x86_64,arm }
+
+{ Written by Jonas Maebe in 2010, released into the public domain }
+
+{$mode delphi}
+{$modeswitch objectivec1}
+
+uses
+  CocoaAll;
+
+var
+  arr: NSMutableArray;
+  element: NSString;
+  pool: NSAutoreleasePool;
+  i: longint;
+begin
+  pool:=NSAutoreleasePool.alloc.init;
+  arr:=NSMutableArray.arrayWithObjects(
+    NSSTR('One'),
+    NSSTR('Two'),
+    NSSTR('Three'),
+    NSSTR('Four'),
+    NSSTR('Five'),
+    NSSTR('Six'),
+    NSSTR('Seven'),
+    nil);
+
+  i:=0;
+  for element in arr do
+    begin
+      inc(i);
+      if i=2 then
+        continue;
+      if i=5 then
+        break;
+      if i in [2,5..10] then
+        halt(1);
+      NSLog(NSSTR('element: %@'),element);
+    end;
+  pool.release;
+end.

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů