2017-08-14 4 views
8

J'ai réussi à réduire ce problème à ceci:Variable locale brisée par la capture de fermeture en cas d'accès dans la méthode imbriquée

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. 

Ici la variable capturedString est corrompue quand on y accède à partir d'une méthode imbriquée. Une compilation Win32 lève EIntOverflow, une compilation Win64 écrit une chaîne vide (corrompue) - la construction peut être provoquée en AV ou d'autres exceptions avec quelques manipulations mais dans tous les cas la référence à la variable locale est corrompue lors de la procédure Nested.

Cela semble se produire uniquement si capturedString est capturé dans une fermeture.

Qu'est-ce qui ne va pas?

Répondre

3

Cela semble être un bug du compilateur:

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

Une solution consiste à utiliser une seconde variable pour la capture dans la méthode anonyme:

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;