Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Compiler/Delphi/Code Generation/Optimization    [ Add a report in this area ]  
Report #:  106213   Status: Closed
Enumerator is not released when used together with GOTO
Project:  Delphi Build #:  14.0.3593.25826
Version:    14.2 Submitted By:   Zigmund Bulinsh
Report Type:  Crash / Data loss / Total failure Date Reported:  6/6/2012 2:07:49 AM
Severity:    Serious / Highly visible problem Last Updated: 6/7/2012 6:04:28 PM
Platform:    All platforms Internal Tracking #:  
Resolution: Cannot Reproduce (Resolution Comments) Resolved in Build: : XE2
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
Description
When using GOTO statement to exit nested loops where enumerators are used, it leads to memory leaks of enumerator object and in some cases to app crash.

You can comment out FastMM4 unit, it does not affect behaviour.

Running this application leads to crash on termination (after Readln). Once you do don't use label - everything works fine.
Steps to Reproduce:
program EnumeratorLeakage;

{$APPTYPE CONSOLE}

{.$DEFINE WORKAROUND}

uses
  FastMM4, Generics.Collections;

type
  TMyObject = class;

  TMyList = TList<TMyObject>;

  TMyObject = class
  public
    Children: TMyList;
    constructor Create; virtual;
    destructor Destroy; override;
  end;

var
  O1, O2, A, B: TMyObject;
  List1, List2: TMyList;
  Flag: Boolean;
  {$IFDEF WORKAROUND}
  ExitLoop: Boolean;
  {$ENDIF}

{$IFNDEF WORKAROUND}
label
  Test;
{$ENDIF}

{ TMyObject }

constructor TMyObject.Create;
begin
  Children := TMyList.Create;
end;

destructor TMyObject.Destroy;
begin
  Children.Free;
  inherited;
end;

begin
  FastMM4.SuppressMessageBoxes := False;
  List1 := TMyList.Create;
  try
    O1 := TMyObject.Create;
    try
      List1.Add(O1);
      List2 := TMyList.Create;
      try
        O2 := TMyObject.Create;
        try
          O1.Children.Add(O2);
          {$IFNDEF WORKAROUND}
          Test:
          if not Flag then
          {$ELSE}
          ExitLoop := False;
          {$ENDIF}
            for A in List1 do
              begin
                for B in A.Children do
                  begin
                    {$IFNDEF WORKAROUND}
                    Flag := True;
                    goto Test;
                    {$ELSE}
                    ExitLoop := True;
                    Break;
                    {$ENDIF}
                  end;
                {$IFDEF WORKAROUND}
                if ExitLoop then
                  Break;
                {$ENDIF}
              end;
        finally
          O2.Free;
        end;
      finally
        List2.Free;
      end;
    finally
      O1.Free;
    end;
  finally
    List1.Free;
  end;
  Readln;
end.
Workarounds
Remove "." in "{.$DEFINE WORKAROUND}" to remove usage of label and replace it with flag.
Attachment
None
Comments

Tomohiro Takahashi at 6/6/2012 7:08:30 PM -
Does your issue still occur with Delphi XE2 Update 4(or Trial version)?

I tried to compile your code with XE2, but I got compile error as below.
-------
[DCC Error] Project2.dpr(74): E2127 'GOTO Test' leads into or out of TRY statement, or out of FOR-IN statement whose enumerator has destructor
-------

Zigmund Bulinsh at 6/6/2012 11:06:34 PM -
Hi!

Thanks for your reply. In 2010 we do not get this error - we get memory leak (as enumerators are not released).
Is that too complicated to handle "goto" compilation with calling destructors of enumerators and then actual jump code?
I knew only that there is no possibility to use goto i try/except blocks, but this one about for-IN is not documented (at least I did not found such).
I just used the workaround, so actually if it works in XE2 then it's fine for me. But I will preffer to have possibility to use GOTO to exit nested loops with enumerators having destructors...

Server Response from: ETNACODE01