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?
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With