21

I'm trying to figure out whether it is possible to initialise a record containing a dynamic array using the "implicit" class operator in Delphi (Berlin 10.1 upd 1)

The attached program produces the following output:

ci iA r1 r2 r3
1  1  1  1  49694764491115752
2  2  2  2  11570520
3  3  3  3  0
4  4  4  4  0
5  5  5  5  0
  • TRec is the record type containing a dynamic array that I want to initialise.
  • ci is a constant array of integer.
  • ia is a dynamic array of integer.
  • r1,r2,r3 are records of type TRec which are initialised in different ways.

As you can see from the output, the first two assignments (r1,r2), using constants work as expected. The third assignment r3 := iArray is accepted by the compiler, but the result is broken. The debugger shows that the value of v in TRec.Implicit is already wrong.

What is going wrong here? Is this possible at all?

program Project5;

{$APPTYPE CONSOLE}

{$R *.res}

type
  TRec = record
    iArray: array of UInt64;
    class operator Implicit(const v: array of UInt64): TRec;
  end;

{ TRec }

class operator TRec.Implicit(const v: array of UInt64): TRec;
var
  i: integer;
begin
  setlength(Result.iArray, Length(v));
  for i := 0 to High(v) do
    Result.iArray[i] := v[i];
end;

const
  ciArray: array [0 .. 4] of UInt64 = (1, 2, 3, 4, 5);

var
  i         : integer;
  iArray    : array of UInt64;
  r1, r2, r3: TRec;

begin
  iArray := [1, 2, 3, 4, 5];

  r1 := [1, 2, 3, 4, 5];
  r2 := ciArray;
  r3 := iArray;

  Writeln('ci iA r1 r1 r3');
  for I := 0 to High(ciArray) do
    Writeln(ciArray[i], '  ', iArray[i], '  ', r1.iArray[i], '  ', r2.iArray[i], '  ', r3.iArray[i]);

  readln;

end.
Lübbe Onken
  • 526
  • 2
  • 12
  • 1
    Sure it is possible. I do it for my `BigInteger` type (http://www.rvelthuis.de/programs/bigintegers.html) too. But make sure that you perform some kind of Copy-On-Write (COW), so each time the **array** is modified, you must ensure it is a unique copy. – Rudy Velthuis Oct 12 '16 at 10:16
  • @RudyVelthuis The compiler bug makes it not possible though, doesn't it. Unless you work around that bug or get a fix from the vendor. – David Heffernan Oct 12 '16 at 11:01
  • Well, it seems that dynarray *parameters* to `Implicit()` are a special case. Otherwise it works. The record can contain a dynarray alright. – Rudy Velthuis Oct 12 '16 at 21:12
  • Well, dyn array parameters are the subject at hand so let's focus on that – David Heffernan Oct 13 '16 at 21:24
  • The subject of the question is "records with a dynarray and and implicit operator," not "records with an implicit operator with open array parameter". Not the same. And this seems to be fixed in Delphi 10.2 Tokyo. – Rudy Velthuis Jul 01 '17 at 04:56
  • similar bug: https://quality.embarcadero.com/browse/RSP-36156 – Gabriel Jun 09 '22 at 10:06

1 Answers1

19

Looks like you found a bug with the codegen there (and it also exists in the Win64 Compiler). I looked through the generated asm and it seems the compiler produces a wrong instruction for the operator overload instead. That is why wrong values end up in the array inside the operator overload. Please report this in Quality Portal.

Code generated for the bad result:

Project109.dpr.46: r3 := iArray;
0040B1F2 A1FC044100       mov eax,[$004104fc]
0040B1F7 8945E8           mov [ebp-$18],eax
0040B1FA 837DE800         cmp dword ptr [ebp-$18],$00
0040B1FE 740B             jz $0040b20b
0040B200 8B45E8           mov eax,[ebp-$18]
0040B203 83E804           sub eax,$04
0040B206 8B00             mov eax,[eax]
0040B208 8945E8           mov [ebp-$18],eax
0040B20B 8D4DD8           lea ecx,[ebp-$28]
0040B20E 8B55E8           mov edx,[ebp-$18]
0040B211 4A               dec edx
0040B212 B8FC044100       mov eax,$004104fc // <-- wrong one
0040B217 E87CF5FFFF       call TRec.&op_Implicit

Code for an equal method:

Project109.dpr.47: r3 := TRec.Implicit(iArray);
0040B22F A1FC044100       mov eax,[$004104fc]
0040B234 8945E4           mov [ebp-$1c],eax
0040B237 837DE400         cmp dword ptr [ebp-$1c],$00
0040B23B 740B             jz $0040b248
0040B23D 8B45E4           mov eax,[ebp-$1c]
0040B240 83E804           sub eax,$04
0040B243 8B00             mov eax,[eax]
0040B245 8945E4           mov [ebp-$1c],eax
0040B248 8D4DD4           lea ecx,[ebp-$2c]
0040B24B 8B55E4           mov edx,[ebp-$1c]
0040B24E 4A               dec edx
0040B24F A1FC044100       mov eax,[$004104fc] // <-- correct one
0040B254 E8CFF5FFFF       call TRec.Implicit

However you can avoid this by adding another overload for the Implicit operator with the parameter type TArray<UInt64> and then also declare your local variable as that type so the compiler choses the correct overload (the one it does not generate wrong code for in this case).

But be aware that this will only work when you pass variables of the type TArray<UInt64> and call the wrong one when you have any other dynamic array of UInt64 because of Delphis strict type rules.

Update: This defect was reported in RSP-16084 and fixed in Delphi 10.2 Tokyo.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102