On 03.07.2018 19:53, Stefan Glienke wrote:
SetLength should not cause anything uninitialized.

No, SetLength is not documented to clear the new initialized memory block so the new memory block can contain whatever you want.

It is documented not to touch the existing memory block in the array.

This is the case of Delphi and FPC. See GetS_Wrong2 in the second example below.

It enlarges or shrinks the data and keeps any prior data as it was, new 
allocated memory is zeroed (at least that is how it works in the Delphi RTL and 
I would be very surprised if FPC would do any different).

The core issue imo is temp var reuse the compiler does which causes hidden data 
reusage/copy which is only an issue with dynamic arrays because they don't have 
CoW semantic when writing to some index. So the temp variable still points to 
the first array which then gets passed to the next function as result. There a 
call to SetLength applies copy on write semantics carrying over any content 
from the previous array that is still in the temp variable to the newly 
allocated array.

I think we discussed this on friday - maybe I did not make my point clear 
enough.

In your case I am entirely on the side with the FPC team. The result variable is not guaranteed to be cleared:

function DoSomething (len: Integer): Vector;
begin
  SetLength(Result, len); // << Result can be everything before the SetLength call
end;

So Result can have garbage data which stays there after the SetLength call.

This happens also in Delphi. Try this code:

program Project1;
{$APPTYPE CONSOLE}
var
  S_wrong, S_correct: string;
  I: Integer;
  function GetS_Wrong: string;
  begin
    Result := result+'X';
  end;
  function GetS_Correct: string;
  begin
    Result := 'X';
  end;
begin
  for I := 0 to 1 do
  begin
    S_wrong := S_wrong + GetS_Wrong;
    S_correct := S_correct + GetS_Correct;
  end;
  Writeln('Wrong: ', S_wrong);
  Writeln('Correct: ', S_correct);
  Readln;
end.

Output (in Delphi XE2):
Wrong: XXX
Correct: XX

----

In case of arrays try this code in Delphi 10:

program Project1;
{$APPTYPE CONSOLE}
type
  Vector = array of AnsiChar;
var
  W: AnsiChar = '1';
  W2: AnsiChar = '1';
  C: AnsiChar = '1';
  function GetS_Wrong: Vector;
  begin
    if Length(Result)<>1 then
      SetLength(Result, 1);
    Result[0] := W;
    Inc(W);
  end;
  function GetS_Wrong2: Vector;
  var
    ChangeNeeded: Boolean;
  begin
    ChangeNeeded := Length(Result)<>1;
    SetLength(Result, 1);
    if ChangeNeeded then
      Result[0] := W2;
    Inc(W2);
  end;
  function GetS_Correct: Vector;
  begin
    SetLength(Result, 1); // set length does a copy -> correct
    Result[0] := C;
    Inc(C);
  end;
  procedure WriteVec(Vec: Vector);
  var
    I: Integer;
  begin
    for I := Low(Vec) to High(Vec) do
      Write(Vec[I]);
  end;
var
  S_wrong, S_wrong2, S_correct: Vector;
  I: Integer;
begin
  for I := 0 to 1 do
  begin
    S_wrong := S_wrong + GetS_Wrong;
    S_wrong2 := S_wrong2 + GetS_Wrong2;
    S_correct := S_correct + GetS_Correct;
  end;
  Write('Wrong: ');
  WriteVec(S_wrong);
  Writeln(' expected: 12');

  Write('Wrong2: ');
  WriteVec(S_wrong2);
  Writeln(' expected: 12');

  Write('Correct: ');
  WriteVec(S_correct);
  Readln;
end.

The output is (Delphi 10):

Wrong: 22 expected 12
Wrong2: 11 expected: 12
Correct: 12

You see that in case of GetS_Wrong and GetS_Wrong2 even Delphi has garbage data in Result. And it is correct like this.

Ondrej
_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply via email to