From 88af29315501096f8863644317fe1a8c890ceef4 Mon Sep 17 00:00:00 2001 From: svenbarth <pascaldragon@googlemail.com> Date: Tue, 25 Sep 2012 09:45:25 +0000 Subject: [PATCH] Fix for Mantis #22160 The cause of the internal error was the following: We have a generic in an unit ("A") which uses another unit ("B") in the implementation section and this other unit uses unit A in the interface section. Now the generic is specialized in the interface section of B. This leads to the problem that in unit A when it tries to load the globalsymtable of unit B that globalsymtable will be Nil, because parsing of the interface section is not yet finished. Thus the change in pgenutil.pas, specialization_init: if the unit is still "in_interface" the localsymtable needs to be used instead of the globalsymtable. This doesn't necessarily lead to a compiling test though, as there is the following possibility: Unit A contains a generic class/record (with methods) and uses unit B in the implementation section. This unit B also contains a generic class/record (with methods) and uses unit A in the implementation section. Both units contain a specialization of the other unit's generic outside of it's own generics (such that generate_specialization is fully triggered). Let's assume compilation starts with unit A and we reach the uses of unit B. Now compilation switches to unit B and completes as unit A is already registered and in compilation. The problem now is that the generic in unit A still contains unresolved forward declarations as the implementation section of A was not yet parsed which will lead to "forward declaration not solved" errors (Note: Delphi compiles this). The solution to this is the following: if a generic is specialized from another unit which is not in state ms_compiled then the unit of the specialization needs to wait for the unit of the generic. So the specialization's unit adds itself into a list of waiting units of the generic's unit. Now inside "proc_unit" we need to check whether this module is waiting for other modules and if so avoid "finishing" the unit (which means generating the methods of the specialization, generating assembler code and ultimately freeing the scanner and PPU). Now when the generic's unit finishes we need to check whether other modules are waiting for it and finish them (of course it's a bit more complicated in reality, but that pretty much sums it up). + globstat.pas: Added an unit which handles the saving and restoring of the global state which was originally inside "parser.pas, compile" so that Don't Repeat Yourself (DRY) is respected. * fmodule.pas, tmodule: + add fields to keep track of the units the module is waiting for and which modules are waiting for the module + add field for the saved global state (raw pointer to avoid circles) + add field for the state which is needed to finish the unit (raw pointer to avoid circles) + move the code which was used in "parser.pas, compile" after a module was successfully compiled to the new virtual method "end_of_parsing" + fppu.pas, tppumodule.end_of_parsing: free the ppufile here * pgenutil.pas: + add new procedure "maybe_add_waiting_unit" which adds the specialization's unit to the waiting list of the generic if that unit is not yet compiled * generate_specialization: call the new function when we add a new (true) specialization * specialization_init: instead of not adding implementation units at all check whether the unit is still parsing the interface section and add the localsymtable in that case * pmodules.pas: * change "proc_unit" to a function which returns "true" if the unit was already finished (no need to wait for other units) + move the code from "proc_unit" from "generate_specialization_procs" on to a new procedure "finish_unit" which * this procedure is either called immediately in "proc_unit" if the unit does not need to wait for other units or from "finish_unit" itself if a unit that is waiting for the given unit does no longer wait for another module (special care is taken in proc_unit to avoid circles) * parser.pas, compile: * correctly handle the case if an unit is not finished * use the new global state functionality from globstat.pas * pay special attention when calling "set_current_module" (see comment at that call) + add tests from 22160 + add test for above mentioned "diamond" case git-svn-id: trunk@22452 - --- .gitattributes | 9 +++ compiler/fmodule.pas | 49 +++++++++++- compiler/fppu.pas | 16 ++++ compiler/globstat.pas | 158 +++++++++++++++++++++++++++++++++++++ compiler/parser.pas | 154 +++++++++--------------------------- compiler/pgenutil.pas | 50 ++++++++++-- compiler/pmodules.pas | 160 +++++++++++++++++++++++++++++++------- compiler/pp.lpi | 15 +++- tests/test/tgeneric91.pp | 11 +++ tests/test/ugeneric91a.pp | 35 +++++++++ tests/test/ugeneric91b.pp | 35 +++++++++ tests/webtbs/tw22160a1.pp | 22 ++++++ tests/webtbs/tw22160b1.pp | 21 +++++ tests/webtbs/uw22160a2.pp | 14 ++++ tests/webtbs/uw22160b2.pp | 11 +++ tests/webtbs/uw22160b3.pp | 14 ++++ 16 files changed, 616 insertions(+), 158 deletions(-) create mode 100644 compiler/globstat.pas create mode 100644 tests/test/tgeneric91.pp create mode 100644 tests/test/ugeneric91a.pp create mode 100644 tests/test/ugeneric91b.pp create mode 100644 tests/webtbs/tw22160a1.pp create mode 100644 tests/webtbs/tw22160b1.pp create mode 100644 tests/webtbs/uw22160a2.pp create mode 100644 tests/webtbs/uw22160b2.pp create mode 100644 tests/webtbs/uw22160b3.pp diff --git a/.gitattributes b/.gitattributes index 92f78d3b90..777126bf0a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -157,6 +157,7 @@ compiler/fppu.pas svneol=native#text/plain compiler/gendef.pas svneol=native#text/plain compiler/generic/cpuinfo.pas svneol=native#text/plain compiler/globals.pas svneol=native#text/plain +compiler/globstat.pas svneol=native#text/pascal compiler/globtype.pas svneol=native#text/plain compiler/hlcg2ll.pas svneol=native#text/plain compiler/hlcgobj.pas svneol=native#text/plain @@ -10816,6 +10817,7 @@ tests/test/tgeneric88.pp svneol=native#text/pascal tests/test/tgeneric89.pp svneol=native#text/pascal tests/test/tgeneric9.pp svneol=native#text/plain tests/test/tgeneric90.pp svneol=native#text/pascal +tests/test/tgeneric91.pp svneol=native#text/pascal tests/test/tgoto.pp svneol=native#text/plain tests/test/theap.pp svneol=native#text/plain tests/test/theapthread.pp svneol=native#text/plain @@ -11399,6 +11401,8 @@ tests/test/ugeneric7.pp svneol=native#text/plain tests/test/ugeneric74a.pp svneol=native#text/pascal tests/test/ugeneric74b.pp svneol=native#text/pascal tests/test/ugeneric75.pp svneol=native#text/pascal +tests/test/ugeneric91a.pp svneol=native#text/pascal +tests/test/ugeneric91b.pp svneol=native#text/pascal tests/test/uhintdir.pp svneol=native#text/plain tests/test/uhlp3.pp svneol=native#text/pascal tests/test/uhlp31.pp svneol=native#text/pascal @@ -12837,6 +12841,8 @@ tests/webtbs/tw2210.pp svneol=native#text/plain tests/webtbs/tw22133.pp svneol=native#text/plain tests/webtbs/tw2214.pp svneol=native#text/plain tests/webtbs/tw22154.pp svneol=native#text/pascal +tests/webtbs/tw22160a1.pp svneol=native#text/pascal +tests/webtbs/tw22160b1.pp svneol=native#text/pascal tests/webtbs/tw2220.pp svneol=native#text/plain tests/webtbs/tw2226.pp svneol=native#text/plain tests/webtbs/tw2229.pp svneol=native#text/plain @@ -13661,6 +13667,9 @@ tests/webtbs/uw20909b.pas svneol=native#text/pascal tests/webtbs/uw20940.pp svneol=native#text/pascal tests/webtbs/uw21808a.pp svneol=native#text/plain tests/webtbs/uw21808b.pp svneol=native#text/plain +tests/webtbs/uw22160a2.pp svneol=native#text/pascal +tests/webtbs/uw22160b2.pp svneol=native#text/pascal +tests/webtbs/uw22160b3.pp svneol=native#text/pascal tests/webtbs/uw2266a.inc svneol=native#text/plain tests/webtbs/uw2266b.pas svneol=native#text/plain tests/webtbs/uw2269.inc svneol=native#text/plain diff --git a/compiler/fmodule.pas b/compiler/fmodule.pas index 1d53b4fd02..d4b3aaf325 100644 --- a/compiler/fmodule.pas +++ b/compiler/fmodule.pas @@ -185,6 +185,17 @@ interface tobjectdef instances (the helper defs) } extendeddefs: TFPHashObjectList; + { this contains a list of units that needs to be waited for until the + unit can be finished (code generated, etc.); this is needed to handle + specializations in circular unit usages correctly } + waitingforunit: tfpobjectlist; + { this contains a list of all units that are waiting for this unit to be + finished } + waitingunits: tfpobjectlist; + + finishstate: pointer; + globalstate: pointer; + namespace: pshortstring; { for JVM target: corresponds to Java package name } { for targets that initialise typed constants via explicit assignments @@ -209,6 +220,7 @@ interface function derefidx_unit(id:longint):longint; function resolve_unit(id:longint):tmodule; procedure allunitsused; + procedure end_of_parsing;virtual; procedure setmodulename(const s:string); procedure AddExternalImport(const libname,symname,symmangledname:string;OrdNr: longint;isvar:boolean;ImportByOrdinalOnly:boolean); property ImportLibraryList : TFPHashObjectList read FImportLibraryList; @@ -534,7 +546,9 @@ implementation ansistrdef:=nil; wpoinfo:=nil; checkforwarddefs:=TFPObjectList.Create(false); - extendeddefs := TFPHashObjectList.Create(true); + extendeddefs:=TFPHashObjectList.Create(true); + waitingforunit:=tfpobjectlist.create(false); + waitingunits:=tfpobjectlist.create(false); globalsymtable:=nil; localsymtable:=nil; globalmacrosymtable:=nil; @@ -622,6 +636,8 @@ implementation stringdispose(mainname); FImportLibraryList.Free; extendeddefs.Free; + waitingforunit.free; + waitingunits.free; stringdispose(asmprefix); stringdispose(deprecatedmsg); stringdispose(namespace); @@ -962,6 +978,37 @@ implementation end; end; + procedure tmodule.end_of_parsing; + begin + { free asmdata } + if assigned(asmdata) then + begin + asmdata.free; + asmdata:=nil; + end; + + { free scanner } + if assigned(scanner) then + begin + if current_scanner=tscannerfile(scanner) then + current_scanner:=nil; + tscannerfile(scanner).free; + scanner:=nil; + end; + + { free symtable stack } + if assigned(symtablestack) then + begin + symtablestack.free; + symtablestack:=nil; + end; + if assigned(macrosymtablestack) then + begin + macrosymtablestack.free; + macrosymtablestack:=nil; + end; + end; + procedure tmodule.setmodulename(const s:string); begin diff --git a/compiler/fppu.pas b/compiler/fppu.pas index fb16cc2bfe..b9507e6bc0 100644 --- a/compiler/fppu.pas +++ b/compiler/fppu.pas @@ -64,6 +64,7 @@ interface function needrecompile:boolean; procedure setdefgeneration; procedure reload_flagged_units; + procedure end_of_parsing;override; private { Each time a unit's defs are (re)created, its defsgeneration is set to the value of a global counter, and the global counter is @@ -1493,6 +1494,21 @@ var end; end; + procedure tppumodule.end_of_parsing; + begin + { module is now compiled } + state:=ms_compiled; + + { free ppu } + if assigned(ppufile) then + begin + ppufile.free; + ppufile:=nil; + end; + + inherited end_of_parsing; + end; + procedure tppumodule.loadppu; const diff --git a/compiler/globstat.pas b/compiler/globstat.pas new file mode 100644 index 0000000000..124a9f0d8c --- /dev/null +++ b/compiler/globstat.pas @@ -0,0 +1,158 @@ +{ + Copyright (c) 2012 by the FPC development team + + Contains functionality to save/restore the global compiler state when + switching between the compilation of different units. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + **************************************************************************** +} +unit globstat; + +{$i fpcdefs.inc} + +interface + +uses + globtype,tokens,globals, + aasmdata, + dbgbase, + symbase,symsym, + fmodule, + scanner,scandir, + procinfo; + + +type + pglobalstate=^tglobalstate; + tglobalstate=record + { scanner } + oldidtoken, + oldtoken : ttoken; + oldtokenpos : tfileposinfo; + oldc : char; + oldpattern, + oldorgpattern : string; + old_block_type : tblock_type; + { symtable } + oldsymtablestack, + oldmacrosymtablestack : TSymtablestack; + oldaktprocsym : tprocsym; + { cg } + oldparse_only : boolean; + { akt.. things } + oldcurrent_filepos : tfileposinfo; + old_current_module : tmodule; + oldcurrent_procinfo : tprocinfo; + old_settings : tsettings; + old_switchesstatestack : tswitchesstatestack; + old_switchesstatestackpos : Integer; + + { only saved/restored if "full" is true } + old_asmdata : tasmdata; + old_debuginfo : tdebuginfo; + old_scanner : tscannerfile; + old_parser_file : string; + end; + +procedure save_global_state(out state:tglobalstate;full:boolean); +procedure restore_global_state(const state:tglobalstate;full:boolean); + +implementation + +uses + pbase; + + procedure save_global_state(out state:tglobalstate;full:boolean); + begin + with state do + begin + old_current_module:=current_module; + + { save symtable state } + oldsymtablestack:=symtablestack; + oldmacrosymtablestack:=macrosymtablestack; + oldcurrent_procinfo:=current_procinfo; + + { save scanner state } + oldc:=c; + oldpattern:=pattern; + oldorgpattern:=orgpattern; + oldtoken:=token; + oldidtoken:=idtoken; + old_block_type:=block_type; + oldtokenpos:=current_tokenpos; + old_switchesstatestack:=switchesstatestack; + old_switchesstatestackpos:=switchesstatestackpos; + + { save cg } + oldparse_only:=parse_only; + + { save akt... state } + { handle the postponed case first } + //flushpendingswitchesstate; + oldcurrent_filepos:=current_filepos; + old_settings:=current_settings; + + if full then + begin + old_asmdata:=current_asmdata; + old_debuginfo:=current_debuginfo; + old_parser_file:=parser_current_file; + old_scanner:=current_scanner; + end; + end; + end; + + + procedure restore_global_state(const state:tglobalstate;full:boolean); + begin + with state do + begin + { restore scanner } + c:=oldc; + pattern:=oldpattern; + orgpattern:=oldorgpattern; + token:=oldtoken; + idtoken:=oldidtoken; + current_tokenpos:=oldtokenpos; + block_type:=old_block_type; + switchesstatestack:=old_switchesstatestack; + switchesstatestackpos:=old_switchesstatestackpos; + + { restore cg } + parse_only:=oldparse_only; + + { restore symtable state } + symtablestack:=oldsymtablestack; + macrosymtablestack:=oldmacrosymtablestack; + current_procinfo:=oldcurrent_procinfo; + current_filepos:=oldcurrent_filepos; + current_settings:=old_settings; + + if full then + begin + current_module:=old_current_module; {!} + current_asmdata:=old_asmdata; + current_debuginfo:=old_debuginfo; + current_scanner:=old_scanner; + parser_current_file:=old_parser_file; + end; + end; + end; + +end. + diff --git a/compiler/parser.pas b/compiler/parser.pas index 56a2a5cfdf..7a14e23bb6 100644 --- a/compiler/parser.pas +++ b/compiler/parser.pas @@ -41,7 +41,7 @@ implementation fksysutl, {$ENDIF} cutils,cclasses, - globtype,version,tokens,systems,globals,verbose,switches, + globtype,version,tokens,systems,globals,verbose,switches,globstat, symbase,symtable,symdef,symsym, finput,fmodule,fppu, aasmbase,aasmtai,aasmdata, @@ -259,35 +259,10 @@ implementation *****************************************************************************} procedure compile(const filename:string); - type - polddata=^tolddata; - tolddata=record - { scanner } - oldidtoken, - oldtoken : ttoken; - oldtokenpos : tfileposinfo; - oldc : char; - oldpattern, - oldorgpattern : string; - old_block_type : tblock_type; - { symtable } - oldsymtablestack, - oldmacrosymtablestack : TSymtablestack; - oldaktprocsym : tprocsym; - { cg } - oldparse_only : boolean; - { akt.. things } - oldcurrent_filepos : tfileposinfo; - old_current_module : tmodule; - oldcurrent_procinfo : tprocinfo; - old_settings : tsettings; - old_switchesstatestack : tswitchesstatestack; - old_switchesstatestackpos : Integer; - end; - var - olddata : polddata; + olddata : pglobalstate; hp,hp2 : tmodule; + finished : boolean; begin { parsing a procedure or declaration should be finished } if assigned(current_procinfo) then @@ -300,35 +275,9 @@ implementation stack. This is needed because compile() can be called recursively } new(olddata); - with olddata^ do - begin - old_current_module:=current_module; - - { save symtable state } - oldsymtablestack:=symtablestack; - oldmacrosymtablestack:=macrosymtablestack; - oldcurrent_procinfo:=current_procinfo; - - { save scanner state } - oldc:=c; - oldpattern:=pattern; - oldorgpattern:=orgpattern; - oldtoken:=token; - oldidtoken:=idtoken; - old_block_type:=block_type; - oldtokenpos:=current_tokenpos; - old_switchesstatestack:=switchesstatestack; - old_switchesstatestackpos:=switchesstatestackpos; - - { save cg } - oldparse_only:=parse_only; - - { save akt... state } - { handle the postponed case first } - flushpendingswitchesstate; - oldcurrent_filepos:=current_filepos; - old_settings:=current_settings; - end; + { handle the postponed case first } + flushpendingswitchesstate; + save_global_state(olddata^,false); { reset parser, a previous fatal error could have left these variables in an unreliable state, this is important for the IDE } @@ -385,6 +334,9 @@ implementation { read the first token } current_scanner.readtoken(false); + { this is set to false if a unit needs to wait for other units } + finished:=true; + { If the compile level > 1 we get a nice "unit expected" error message if we are trying to use a program as unit.} try @@ -392,7 +344,7 @@ implementation if (token=_UNIT) or (compile_level>1) then begin current_module.is_unit:=true; - proc_unit; + finished:=proc_unit; end else if (token=_ID) and (idtoken=_PACKAGE) then begin @@ -412,45 +364,24 @@ implementation raise; end; end; + + { the program or the unit at the command line should not need to wait + for other units } + if (compile_level=1) and not finished then + internalerror(2012091901); finally if assigned(current_module) then begin - { module is now compiled } - tppumodule(current_module).state:=ms_compiled; - - { free ppu } - if assigned(tppumodule(current_module).ppufile) then + if finished then + current_module.end_of_parsing + else begin - tppumodule(current_module).ppufile.free; - tppumodule(current_module).ppufile:=nil; - end; - - { free asmdata } - if assigned(current_module.asmdata) then - begin - current_module.asmdata.free; - current_module.asmdata:=nil; - end; - - { free scanner } - if assigned(current_module.scanner) then - begin - if current_scanner=tscannerfile(current_module.scanner) then - current_scanner:=nil; - tscannerfile(current_module.scanner).free; - current_module.scanner:=nil; - end; - - { free symtable stack } - if assigned(symtablestack) then - begin - symtablestack.free; - symtablestack:=nil; - end; - if assigned(macrosymtablestack) then - begin - macrosymtablestack.free; + { these are saved in the unit's state and thus can be set to + Nil again as would be done by tmodule.end_of_parsing } macrosymtablestack:=nil; + symtablestack:=nil; + if current_scanner=current_module.scanner then + current_scanner:=nil; end; end; @@ -459,33 +390,13 @@ implementation { Write Browser Collections } do_extractsymbolinfo; - with olddata^ do - begin - { restore scanner } - c:=oldc; - pattern:=oldpattern; - orgpattern:=oldorgpattern; - token:=oldtoken; - idtoken:=oldidtoken; - current_tokenpos:=oldtokenpos; - block_type:=old_block_type; - switchesstatestack:=old_switchesstatestack; - switchesstatestackpos:=old_switchesstatestackpos; + restore_global_state(olddata^,false); - { restore cg } - parse_only:=oldparse_only; + { Restore all locally modified warning messages } + RestoreLocalVerbosity(current_settings.pmessage); + current_exceptblock:=0; + exceptblockcounter:=0; - { restore symtable state } - symtablestack:=oldsymtablestack; - macrosymtablestack:=oldmacrosymtablestack; - current_procinfo:=oldcurrent_procinfo; - current_filepos:=oldcurrent_filepos; - current_settings:=old_settings; - { Restore all locally modified warning messages } - RestoreLocalVerbosity(current_settings.pmessage); - current_exceptblock:=0; - exceptblockcounter:=0; - end; { Shut down things when the last file is compiled succesfull } if (compile_level=1) and (status.errorcount=0) then @@ -518,7 +429,14 @@ implementation unloaded_units.Clear; end; dec(compile_level); - set_current_module(olddata^.old_current_module); + { If used units are compiled current_module is already the same as + the stored module. Now if the unit is not finished its scanner is + not yet freed and thus set_current_module would reopen the scanned + file which will result in pointing to the wrong position in the + file. In the normal case current_scanner and current_module.scanner + would be Nil, thus nothing bad would happen } + if olddata^.old_current_module<>current_module then + set_current_module(olddata^.old_current_module); FreeLocalVerbosity(current_settings.pmessage); diff --git a/compiler/pgenutil.pas b/compiler/pgenutil.pas index f7c62e79ac..230b870160 100644 --- a/compiler/pgenutil.pas +++ b/compiler/pgenutil.pas @@ -56,7 +56,7 @@ uses { common } cutils,fpccrc, { global } - globals,tokens,verbose, + globals,tokens,verbose,finput, { symtable } symconst,symsym,symtable, { modules } @@ -69,6 +69,34 @@ uses pbase,pexpr,pdecsub,ptype; + procedure maybe_add_waiting_unit(tt:tdef); + var + hmodule : tmodule; + begin + if not assigned(tt) or + not (df_generic in tt.defoptions) then + exit; + + hmodule:=find_module_from_symtable(tt.owner); + if not assigned(hmodule) then + internalerror(2012092401); + + if hmodule=current_module then + exit; + + if hmodule.state<>ms_compiled then + begin +{$ifdef DEBUG_UNITWAITING} + Writeln('Unit ', current_module.modulename^, + ' waiting for ', hmodule.modulename^); +{$endif DEBUG_UNITWAITING} + if current_module.waitingforunit.indexof(hmodule)<0 then + current_module.waitingforunit.add(hmodule); + if hmodule.waitingunits.indexof(current_module)<0 then + hmodule.waitingunits.add(current_module); + end; + end; + procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string;parsedtype:tdef;symname:string); var st : TSymtable; @@ -375,6 +403,8 @@ uses current_specializedef:=nil; end; + maybe_add_waiting_unit(genericdef); + { First a new typesym so we can reuse this specialization and references to this specialization can be handled } srsym:=ttypesym.create(finalspecializename,generrordef); @@ -696,14 +726,18 @@ uses pu:=tused_unit(hmodule.used_units.first); while assigned(pu) do begin - if (hmodule<>current_module) and not pu.in_interface then - begin - pu:=tused_unit(pu.next); - continue; - end; if not assigned(pu.u.globalsymtable) then - internalerror(200705153); - symtablestack.push(pu.u.globalsymtable); + { in certain circular, but valid unit constellations it can happen + that we specialize a generic in a different unit that was used + in the implementation section of the generic's unit and were the + interface is still being parsed and thus the localsymtable is in + reality the global symtable } + if pu.u.in_interface then + symtablestack.push(pu.u.localsymtable) + else + internalerror(200705153) + else + symtablestack.push(pu.u.globalsymtable); sym:=tsym(unitsyms.find(pu.u.modulename^)); if assigned(sym) and not assigned(tunitsym(sym).module) then tunitsym(sym).module:=pu.u; diff --git a/compiler/pmodules.pas b/compiler/pmodules.pas index 9d5b49c7ef..6ae5fd6e13 100644 --- a/compiler/pmodules.pas +++ b/compiler/pmodules.pas @@ -25,7 +25,7 @@ unit pmodules; interface - procedure proc_unit; + function proc_unit:boolean; procedure proc_package; procedure proc_program(islibrary : boolean); @@ -35,7 +35,7 @@ implementation SysUtils, globtype,version,systems,tokens, cutils,cfileutl,cclasses,comphook, - globals,verbose,fmodule,finput,fppu, + globals,verbose,fmodule,finput,fppu,globstat, symconst,symbase,symtype,symdef,symsym,symtable,symcreat, wpoinfo, aasmtai,aasmdata,aasmcpu,aasmbase, @@ -725,44 +725,34 @@ implementation end; {$endif jvm} - procedure proc_unit; +type + tfinishstate=record + init_procinfo:tcgprocinfo; + end; + pfinishstate=^tfinishstate; - function is_assembler_generated:boolean; - var - hal : tasmlisttype; - begin - result:=false; - if Errorcount=0 then - begin - for hal:=low(TasmlistType) to high(TasmlistType) do - if not current_asmdata.asmlists[hal].empty then - begin - result:=true; - exit; - end; - end; - end; + procedure finish_unit(module:tmodule;immediate:boolean);forward; + function proc_unit:boolean; var main_file: tinputfile; + { {$ifdef EXTDEBUG} store_crc, {$endif EXTDEBUG} store_interface_crc, - store_indirect_crc: cardinal; + store_indirect_crc: cardinal;} s1,s2 : ^string; {Saves stack space} - force_init_final : boolean; - init_procinfo, - finalize_procinfo : tcgprocinfo; + init_procinfo : tcgprocinfo; unitname : ansistring; unitname8 : string[8]; - ag: boolean; -{$ifdef debug_devirt} - i: longint; -{$endif debug_devirt} + i,j : longint; + finishstate:pfinishstate; + globalstate:pglobalstate; begin + result:=true; + init_procinfo:=nil; - finalize_procinfo:=nil; if m_mac in current_settings.modeswitches then current_module.mode_switch_allowed:= false; @@ -962,6 +952,99 @@ implementation current_module.mainfilepos:=init_procinfo.entrypos; end; + { remove all units that we are waiting for that are already waiting for + us => breaking up circles } + for i:=0 to current_module.waitingunits.count-1 do + for j:=current_module.waitingforunit.count-1 downto 0 do + if current_module.waitingunits[i]=current_module.waitingforunit[j] then + current_module.waitingforunit.delete(j); + +{$ifdef DEBUG_UNITWAITING} + Writeln('Units waiting for ', current_module.modulename^, ': ', + current_module.waitingforunit.Count); +{$endif} + result:=current_module.waitingforunit.count=0; + + { save all information that is needed for finishing the unit } + New(finishstate); + finishstate^.init_procinfo:=init_procinfo; + current_module.finishstate:=finishstate; + + if result then + finish_unit(current_module,true) + else + begin + { save the current state, so the parsing can continue where we left + of here } + New(globalstate); + save_global_state(globalstate^,true); + current_module.globalstate:=globalstate; + end; + end; + + procedure finish_unit(module:tmodule;immediate:boolean); + + function is_assembler_generated:boolean; + var + hal : tasmlisttype; + begin + result:=false; + if Errorcount=0 then + begin + for hal:=low(TasmlistType) to high(TasmlistType) do + if not current_asmdata.asmlists[hal].empty then + begin + result:=true; + exit; + end; + end; + end; + + procedure module_is_done;inline; + begin + dispose(pglobalstate(current_module.globalstate)); + current_module.globalstate:=nil; + dispose(pfinishstate(current_module.finishstate)); + current_module.finishstate:=nil; + end; + + var +{$ifdef EXTDEBUG} + store_crc, +{$endif EXTDEBUG} + store_interface_crc, + store_indirect_crc: cardinal; + force_init_final : boolean; + init_procinfo, + finalize_procinfo : tcgprocinfo; + i,idx : longint; + ag : boolean; + finishstate : tfinishstate; + globalstate : tglobalstate; + waitingmodule : tmodule; + begin + if not immediate then + begin +{$ifdef DEBUG_UNITWAITING} + writeln('finishing waiting unit ''', module.modulename^, ''''); +{$endif DEBUG_UNITWAITING} + { restore the state when we stopped working on the unit } + save_global_state(globalstate,true); + if not assigned(module.globalstate) then + internalerror(2012091802); + restore_global_state(pglobalstate(module.globalstate)^,true); + end; + + { current_module is now module } + + if not assigned(current_module.finishstate) then + internalerror(2012091801); + finishstate:=pfinishstate(current_module.finishstate)^; + + finalize_procinfo:=nil; + + init_procinfo:=finishstate.init_procinfo; + { Generate specializations of objectdefs methods } generate_specialization_procs; @@ -1061,6 +1144,9 @@ implementation begin Message1(unit_f_errors_in_unit,tostr(Errorcount)); status.skip_error:=true; + module_is_done; + if not immediate then + restore_global_state(globalstate,true); exit; end; @@ -1148,6 +1234,9 @@ implementation begin Message1(unit_f_errors_in_unit,tostr(Errorcount)); status.skip_error:=true; + module_is_done; + if not immediate then + restore_global_state(globalstate,true); exit; end; @@ -1190,6 +1279,23 @@ implementation {$endif debug_devirt} Message1(unit_u_finished_compiling,current_module.modulename^); + + module_is_done; + if not immediate then + restore_global_state(globalstate,true); + + for i:=0 to module.waitingunits.count-1 do + begin + waitingmodule:=tmodule(module.waitingunits[i]); + waitingmodule.waitingforunit.remove(module); + { only finish the module if it isn't already finished } + if (waitingmodule.waitingforunit.count=0) and + assigned(waitingmodule.finishstate) then + begin + finish_unit(waitingmodule,false); + waitingmodule.end_of_parsing; + end; + end; end; diff --git a/compiler/pp.lpi b/compiler/pp.lpi index e6f1afe1ab..9788239ca8 100644 --- a/compiler/pp.lpi +++ b/compiler/pp.lpi @@ -25,11 +25,12 @@ <RunParams> <local> <FormatVersion Value="1"/> - <CommandLineParams Value="-MObjFPC -Scgi -O1 -gl -vewnhi -l -FiD:\programming\laz_svn\fpc_features\cpstr\lib\i386-win32\ -FuD:\programming\laz_svn\cpstr\cpstrnew\ -Fu. -FUD:\programming\laz_svn\fpc_features\cpstr\lib\i386-win32\ -oproject1.exe D:\programming\laz_svn\fpc_features\cpstr\project1.lpr"/> + <CommandLineParams Value="-n -Furtl\units\i386-win32 -viwn -FEtestoutput fpctests\tgenunitstatic.pas"/> <LaunchingApplication PathPlusParams="\usr\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/> + <WorkingDirectory Value="c:\svn\fpc"/> </local> </RunParams> - <Units Count="2"> + <Units Count="3"> <Unit0> <Filename Value="pp.pas"/> <IsPartOfProject Value="True"/> @@ -40,6 +41,11 @@ <IsPartOfProject Value="True"/> <UnitName Value="aasmcpu"/> </Unit1> + <Unit2> + <Filename Value="globstat.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="globstat"/> + </Unit2> </Units> </ProjectOptions> <CompilerOptions> @@ -63,7 +69,7 @@ </Parsing> <Linking> <Debugging> - <DebugInfoType Value="dsStabs"/> + <DebugInfoType Value="dsDwarf2Set"/> </Debugging> </Linking> <Other> @@ -78,7 +84,8 @@ <CompilerMessages> <UseMsgFile Value="True"/> </CompilerMessages> - <CustomOptions Value="-di386"/> + <CustomOptions Value="-di386 +-dDEBUG_UNITWAITING"/> <CompilerPath Value="$(CompPath)"/> </Other> </CompilerOptions> diff --git a/tests/test/tgeneric91.pp b/tests/test/tgeneric91.pp new file mode 100644 index 0000000000..077f315dcf --- /dev/null +++ b/tests/test/tgeneric91.pp @@ -0,0 +1,11 @@ +{ %NORUN } + +program tgeneric91; + +uses + ugeneric91a,ugeneric91b; + +begin + TSomeClass1.Test; + TSomeClass2.Test; +end. diff --git a/tests/test/ugeneric91a.pp b/tests/test/ugeneric91a.pp new file mode 100644 index 0000000000..3594199b96 --- /dev/null +++ b/tests/test/ugeneric91a.pp @@ -0,0 +1,35 @@ +unit ugeneric91a; + +{$mode objfpc}{$H+} + +interface + +type + generic TSomeGeneric1<T> = class + class procedure Test; + end; + + TSomeClass1 = class + class procedure Test; + end; + +implementation + +uses + ugeneric91b; + +type + TSomeGeneric2LongInt = specialize TSomeGeneric2<LongInt>; + +class procedure TSomeClass1.Test; +begin + TSomeGeneric2LongInt.Test; +end; + +class procedure TSomeGeneric1.Test; +begin + Writeln(Self.ClassName); +end; + +end. + diff --git a/tests/test/ugeneric91b.pp b/tests/test/ugeneric91b.pp new file mode 100644 index 0000000000..874b9ac745 --- /dev/null +++ b/tests/test/ugeneric91b.pp @@ -0,0 +1,35 @@ +unit ugeneric91b; + +{$mode objfpc}{$H+} + +interface + +type + generic TSomeGeneric2<T> = class + class procedure Test; + end; + + TSomeClass2 = class + class procedure Test; + end; + +implementation + +uses + ugeneric91a; + +type + TSomeGeneric1LongInt = specialize TSomeGeneric1<LongInt>; + +class procedure TSomeClass2.Test; +begin + TSomeGeneric1LongInt.Test; +end; + +class procedure TSomeGeneric2.Test; +begin + Writeln(Self.ClassName); +end; + +end. + diff --git a/tests/webtbs/tw22160a1.pp b/tests/webtbs/tw22160a1.pp new file mode 100644 index 0000000000..bb99600331 --- /dev/null +++ b/tests/webtbs/tw22160a1.pp @@ -0,0 +1,22 @@ +unit tw22160a1; + +{$mode delphi} + +interface + +type + TWrapper<T> = class + procedure Z; + end; + +implementation + +uses uw22160a2; + +{ TWrapper<T> } + +procedure TWrapper<T>.Z; +begin +end; + +end. diff --git a/tests/webtbs/tw22160b1.pp b/tests/webtbs/tw22160b1.pp new file mode 100644 index 0000000000..0dcf6fface --- /dev/null +++ b/tests/webtbs/tw22160b1.pp @@ -0,0 +1,21 @@ +unit tw22160b1; + +{$mode delphi} + +interface + +type + TWrapper<T> = class + procedure Test; + end; + +implementation + +uses uw22160b2; + +procedure TWrapper<T>.Test; +begin + +end; + +end. diff --git a/tests/webtbs/uw22160a2.pp b/tests/webtbs/uw22160a2.pp new file mode 100644 index 0000000000..e8bd40ec99 --- /dev/null +++ b/tests/webtbs/uw22160a2.pp @@ -0,0 +1,14 @@ +unit uw22160a2; + +{$mode delphi} + +interface + +uses tw22160a1; + +implementation + +type + TByteWrapper = TWrapper<Byte>; + +end. diff --git a/tests/webtbs/uw22160b2.pp b/tests/webtbs/uw22160b2.pp new file mode 100644 index 0000000000..456e23a9c4 --- /dev/null +++ b/tests/webtbs/uw22160b2.pp @@ -0,0 +1,11 @@ +unit uw22160b2; + +{$mode delphi} + +interface + +uses uw22160b3; + +implementation + +end. diff --git a/tests/webtbs/uw22160b3.pp b/tests/webtbs/uw22160b3.pp new file mode 100644 index 0000000000..32fe12f88a --- /dev/null +++ b/tests/webtbs/uw22160b3.pp @@ -0,0 +1,14 @@ +unit uw22160b3; + +{$mode delphi} + +interface + +uses tw22160b1; + +type + TByteWrapper = TWrapper<Byte>; + +implementation + +end.