Skip to content
  • Sven/Sarah Barth's avatar
    Fix for Mantis #22160 · 88af2931
    Sven/Sarah Barth authored
    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 -
    88af2931