Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Local variable broken by closure capture when accessed in nested method

I've managed to reduce this problem to this :

program Project1;
{$APPTYPE CONSOLE}

uses
  SysUtils, Threading;

procedure Foo(AString: string);
var
  LTask : ITask;
  capturedString : string;
  procedure Nested;
  begin
    try
      WriteLn('Nested : ' + capturedString); { ! EIntOverflow (Win32) here }
    except on E : Exception do
      WriteLn(E.Message);  
    end; 
  end;
begin
  capturedString := AString;
  WriteLn('Local : ' + capturedString);
  Nested;
  LTask := TTask.Create(
    procedure
      procedure AnonNested;
      begin
        WriteLn(capturedString); { Removing this eliminates the problem }
      end;
    begin
    end);
end;

begin
  Foo('foo');
  ReadLn;
end.

Here the capturedString variable gets corrupted when accessed from within a nested method. A Win32 compile raises EIntOverflow, a Win64 compile writes out a (corrupt) empty string - either build can be provoked into AV or other exceptions with some manipulation but in all cases the reference to the local variable is corrupted when entering the Nested procedure.

This seems to only happen if capturedString is captured in a closure.

What's going wrong?

like image 850
J... Avatar asked Aug 14 '17 19:08

J...


1 Answers

This seems like a compiler bug that is resolved in 10.2 :

#RSP-18833: Capture by closure corrupts local variable used in nested method

A workaround is to use a second variable for capture in the anonymous method:

procedure Foo(AString: string);
var
  LTask : ITask;
  capturedString, s2 : string;
  procedure Nested;
  begin
    try
      WriteLn('Nested : ' + capturedString);
    except on E : Exception do
      WriteLn(E.Message);  { !!! }
    end; 
  end;
begin
  capturedString := AString;
  s2 := capturedString;
  WriteLn('Local : ' + capturedString);
  Nested;
  LTask := TTask.Create(
    procedure
      procedure AnonNested;
      begin
        WriteLn(s2); { Capture another variable }
      end;
    begin
    end);
end;
like image 121
J... Avatar answered Sep 29 '22 11:09

J...